blob: a350cd035c213e8330d21b8758d8573bc96b6421 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# 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.
#
"""Tests for google.apphosting.tools.devappserver2.module."""
import httplib
import logging
import os
import re
import time
import unittest
import google
import mox
from google.appengine.api import appinfo
from google.appengine.api import request_info
from google.appengine.tools.devappserver2 import api_server
from google.appengine.tools.devappserver2 import application_configuration
from google.appengine.tools.devappserver2 import constants
from google.appengine.tools.devappserver2 import dispatcher
from google.appengine.tools.devappserver2 import instance
from google.appengine.tools.devappserver2 import module
from google.appengine.tools.devappserver2 import start_response_utils
from google.appengine.tools.devappserver2 import wsgi_server
class ModuleConfigurationStub(object):
def __init__(self,
application_root='/root',
application='app',
module_name='default',
automatic_scaling=appinfo.AutomaticScaling(),
version='version',
runtime='python27',
threadsafe=False,
skip_files='',
inbound_services=['warmup'],
handlers=[appinfo.URLMap(url=r'/python-(.*)',
script=r'\1.py')],
normalized_libraries=None,
env_variables=None,
manual_scaling=None,
basic_scaling=None):
self.application_root = application_root
self.application = application
self.module_name = module_name
self.automatic_scaling = automatic_scaling
self.manual_scaling = manual_scaling
self.basic_scaling = basic_scaling
self.major_version = version
self.runtime = runtime
self.threadsafe = threadsafe
self.skip_files = skip_files
self.inbound_services = inbound_services
self.handlers = handlers
self.normalized_libraries = normalized_libraries or []
self.env_variables = env_variables or []
self.version_id = '%s:%s.%s' % (module_name, version, '12345')
self.is_backend = False
def check_for_updates(self):
return set()
class ModuleFacade(module.Module):
def __init__(self,
module_configuration=ModuleConfigurationStub(),
instance_factory=None,
ready=True,
allow_skipped_files=False,
threadsafe_override=None):
super(ModuleFacade, self).__init__(
module_configuration,
host='fakehost',
balanced_port=0,
api_host='localhost',
api_port=8080,
auth_domain='gmail.com',
runtime_stderr_loglevel=1,
php_config=None,
python_config=None,
cloud_sql_config=None,
default_version_port=8080,
port_registry=dispatcher.PortRegistry(),
request_data=None,
dispatcher=None,
max_instances=None,
use_mtime_file_watcher=False,
automatic_restarts=True,
allow_skipped_files=allow_skipped_files,
threadsafe_override=threadsafe_override)
if instance_factory is not None:
self._instance_factory = instance_factory
self._ready = ready
@property
def ready(self):
return self._ready
@property
def balanced_port(self):
return self._balanced_port
class AutoScalingModuleFacade(module.AutoScalingModule):
def __init__(self,
module_configuration=ModuleConfigurationStub(),
balanced_port=0,
instance_factory=None,
max_instances=None,
ready=True):
super(AutoScalingModuleFacade, self).__init__(
module_configuration,
host='fakehost',
balanced_port=balanced_port,
api_host='localhost',
api_port=8080,
auth_domain='gmail.com',
runtime_stderr_loglevel=1,
php_config=None,
python_config=None,
cloud_sql_config=None,
default_version_port=8080,
port_registry=dispatcher.PortRegistry(),
request_data=None,
dispatcher=None,
max_instances=max_instances,
use_mtime_file_watcher=False,
automatic_restarts=True,
allow_skipped_files=False,
threadsafe_override=None)
if instance_factory is not None:
self._instance_factory = instance_factory
self._ready = ready
@property
def ready(self):
return self._ready
@property
def balanced_port(self):
return self._balanced_port
class ManualScalingModuleFacade(module.ManualScalingModule):
def __init__(self,
module_configuration=ModuleConfigurationStub(),
balanced_port=0,
instance_factory=None,
ready=True):
super(ManualScalingModuleFacade, self).__init__(
module_configuration,
host='fakehost',
balanced_port=balanced_port,
api_host='localhost',
api_port=8080,
auth_domain='gmail.com',
runtime_stderr_loglevel=1,
php_config=None,
python_config=None,
cloud_sql_config=None,
default_version_port=8080,
port_registry=dispatcher.PortRegistry(),
request_data=None,
dispatcher=None,
max_instances=None,
use_mtime_file_watcher=False,
automatic_restarts=True,
allow_skipped_files=False,
threadsafe_override=None)
if instance_factory is not None:
self._instance_factory = instance_factory
self._ready = ready
@property
def ready(self):
return self._ready
@property
def balanced_port(self):
return self._balanced_port
class BasicScalingModuleFacade(module.BasicScalingModule):
def __init__(self,
host='fakehost',
module_configuration=ModuleConfigurationStub(),
balanced_port=0,
instance_factory=None,
ready=True):
super(BasicScalingModuleFacade, self).__init__(
module_configuration,
host,
balanced_port=balanced_port,
api_host='localhost',
api_port=8080,
auth_domain='gmail.com',
runtime_stderr_loglevel=1,
php_config=None,
python_config=None,
cloud_sql_config=None,
default_version_port=8080,
port_registry=dispatcher.PortRegistry(),
request_data=None,
dispatcher=None,
max_instances=None,
use_mtime_file_watcher=False,
automatic_restarts=True,
allow_skipped_files=False,
threadsafe_override=None)
if instance_factory is not None:
self._instance_factory = instance_factory
self._ready = ready
@property
def ready(self):
return self._ready
@property
def balanced_port(self):
return self._balanced_port
class BuildRequestEnvironTest(unittest.TestCase):
def setUp(self):
api_server.test_setup_stubs()
self.module = ModuleFacade()
def test_build_request_environ(self):
expected_environ = {
constants.FAKE_IS_ADMIN_HEADER: '1',
'HTTP_HOST': 'fakehost:8080',
'HTTP_HEADER': 'Value',
'HTTP_OTHER': 'Values',
'CONTENT_LENGTH': '4',
'PATH_INFO': '/foo',
'QUERY_STRING': 'bar=baz',
'REQUEST_METHOD': 'PUT',
'REMOTE_ADDR': '1.2.3.4',
'SERVER_NAME': 'fakehost',
'SERVER_PORT': '8080',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.multithread': True,
'wsgi.multiprocess': True}
environ = self.module.build_request_environ(
'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
'body', '1.2.3.4', 8080)
self.assertEqual('', environ.pop('wsgi.errors').getvalue())
self.assertEqual('body', environ.pop('wsgi.input').getvalue())
self.assertEqual(expected_environ, environ)
def test_build_request_environ_fake_is_logged_in(self):
expected_environ = {
constants.FAKE_IS_ADMIN_HEADER: '1',
constants.FAKE_LOGGED_IN_HEADER: '1',
'HTTP_HOST': 'fakehost:8080',
'HTTP_HEADER': 'Value',
'HTTP_OTHER': 'Values',
'CONTENT_LENGTH': '4',
'PATH_INFO': '/foo',
'QUERY_STRING': 'bar=baz',
'REQUEST_METHOD': 'PUT',
'REMOTE_ADDR': '1.2.3.4',
'SERVER_NAME': 'fakehost',
'SERVER_PORT': '8080',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.multithread': True,
'wsgi.multiprocess': True}
environ = self.module.build_request_environ(
'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
'body', '1.2.3.4', 8080, fake_login=True)
self.assertEqual('', environ.pop('wsgi.errors').getvalue())
self.assertEqual('body', environ.pop('wsgi.input').getvalue())
self.assertEqual(expected_environ, environ)
def test_build_request_environ_unicode_body(self):
expected_environ = {
constants.FAKE_IS_ADMIN_HEADER: '1',
'HTTP_HOST': 'fakehost',
'HTTP_HEADER': 'Value',
'HTTP_OTHER': 'Values',
'CONTENT_LENGTH': '4',
'PATH_INFO': '/foo',
'QUERY_STRING': 'bar=baz',
'REQUEST_METHOD': 'PUT',
'REMOTE_ADDR': '1.2.3.4',
'SERVER_NAME': 'fakehost',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.multithread': True,
'wsgi.multiprocess': True}
environ = self.module.build_request_environ(
'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
u'body', '1.2.3.4', 80)
self.assertEqual('', environ.pop('wsgi.errors').getvalue())
self.assertEqual('body', environ.pop('wsgi.input').getvalue())
self.assertEqual(expected_environ, environ)
class TestModuleCreateUrlHandlers(unittest.TestCase):
"""Tests for module.Module._create_url_handlers."""
def setUp(self):
self.module_configuration = ModuleConfigurationStub()
self.instance_factory = instance.InstanceFactory(None, 1)
self.servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration)
self.instance_factory.START_URL_MAP = appinfo.URLMap(
url='/_ah/start',
script='start_handler',
login='admin')
self.instance_factory.WARMUP_URL_MAP = appinfo.URLMap(
url='/_ah/warmup',
script='warmup_handler',
login='admin')
# Built-in: login, blob_upload, blob_image, channel, gcs, endpoints
self.num_builtin_handlers = 6
def test_match_all(self):
self.module_configuration.handlers = [appinfo.URLMap(url=r'.*',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 1, len(handlers))
def test_match_start_only(self):
self.module_configuration.handlers = [appinfo.URLMap(url=r'/_ah/start',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 2, len(handlers))
self.assertEqual(self.instance_factory.WARMUP_URL_MAP, handlers[0].url_map)
def test_match_warmup_only(self):
self.module_configuration.handlers = [appinfo.URLMap(url=r'/_ah/warmup',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 2, len(handlers))
self.assertEqual(self.instance_factory.START_URL_MAP, handlers[0].url_map)
def test_match_neither_warmup_nor_start(self):
self.module_configuration.handlers = [appinfo.URLMap(url=r'/',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 3, len(handlers))
self.assertEqual(self.instance_factory.WARMUP_URL_MAP, handlers[0].url_map)
self.assertEqual(self.instance_factory.START_URL_MAP, handlers[1].url_map)
def test_match_static_only(self):
self.module_configuration.handlers = [
appinfo.URLMap(url=r'/_ah/start', static_dir='foo'),
appinfo.URLMap(url=r'/_ah/warmup', static_files='foo', upload='foo')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 4, len(handlers))
self.assertEqual(self.instance_factory.WARMUP_URL_MAP, handlers[0].url_map)
self.assertEqual(self.instance_factory.START_URL_MAP, handlers[1].url_map)
def test_match_start_only_no_inbound_warmup(self):
self.module_configuration.inbound_services = None
self.module_configuration.handlers = [appinfo.URLMap(url=r'/_ah/start',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 1, len(handlers))
def test_match_warmup_only_no_inbound_warmup(self):
self.module_configuration.inbound_services = None
self.module_configuration.handlers = [appinfo.URLMap(url=r'/_ah/warmup',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 2, len(handlers))
self.assertEqual(self.instance_factory.START_URL_MAP, handlers[0].url_map)
def test_match_neither_warmup_nor_start_no_inbound_warmup(self):
self.module_configuration.inbound_services = None
self.module_configuration.handlers = [appinfo.URLMap(url=r'/',
script=r'foo.py')]
handlers = self.servr._create_url_handlers()
self.assertEqual(self.num_builtin_handlers + 2, len(handlers))
self.assertEqual(self.instance_factory.START_URL_MAP, handlers[0].url_map)
class TestModuleGetRuntimeConfig(unittest.TestCase):
"""Tests for module.Module._get_runtime_config."""
def setUp(self):
self.module_configuration = ModuleConfigurationStub(skip_files='foo')
self.module_configuration.handlers = [
appinfo.URLMap(url=r'/static', static_dir='static'),
appinfo.URLMap(url=r'/app_read_static', static_dir='app_read_static',
application_readable=True),
appinfo.URLMap(url=r'/static_images/*.png',
static_files=r'static_images/\\1',
upload=r'static_images/*.png'),
appinfo.URLMap(url=r'/app_readable_static_images/*.png',
static_files=r'app_readable_static_images/\\1',
upload=r'app_readable_static_images/*.png',
application_readable=True),
]
self.instance_factory = instance.InstanceFactory(None, 1)
def test_static_files_regex(self):
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration)
config = servr._get_runtime_config()
self.assertEqual(r'^(static%s.*)|(static_images/*.png)$' %
re.escape(os.path.sep),
config.static_files)
def test_allow_skipped_files(self):
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration,
allow_skipped_files=True)
config = servr._get_runtime_config()
self.assertFalse(config.HasField('skip_files'))
self.assertFalse(config.HasField('static_files'))
def test_threadsafe_true_override_none(self):
self.module_configuration.threadsafe = True
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration)
config = servr._get_runtime_config()
self.assertTrue(config.threadsafe)
def test_threadsafe_false_override_none(self):
self.module_configuration.threadsafe = False
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration)
config = servr._get_runtime_config()
self.assertFalse(config.threadsafe)
def test_threadsafe_true_override_false(self):
self.module_configuration.threadsafe = True
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration,
threadsafe_override=False)
config = servr._get_runtime_config()
self.assertFalse(config.threadsafe)
def test_threadsafe_false_override_true(self):
self.module_configuration.threadsafe = False
servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration,
threadsafe_override=True)
config = servr._get_runtime_config()
self.assertTrue(config.threadsafe)
class TestModuleShutdownInstance(unittest.TestCase):
"""Tests for module.Module._shutdown_instance."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.module_configuration = ModuleConfigurationStub()
self.instance_factory = instance.InstanceFactory(None, 1)
self.servr = ModuleFacade(instance_factory=self.instance_factory,
module_configuration=self.module_configuration)
self.mox.StubOutWithMock(logging, 'exception')
self.mox.StubOutWithMock(self.servr, '_handle_request')
self.mox.StubOutWithMock(self.servr._quit_event, 'wait')
self.mox.StubOutWithMock(module.Module, 'build_request_environ')
self.inst = self.mox.CreateMock(instance.Instance)
self.time = 0
self.mox.stubs.Set(time, 'time', lambda: self.time)
def tearDown(self):
self.mox.UnsetStubs()
def test_shutdown_instance(self):
def advance_time(*unused_args, **unused_kwargs):
self.time += 10
environ = object()
self.servr.build_request_environ(
'GET', '/_ah/stop', [], '', '0.1.0.3', 9000, fake_login=True).AndReturn(
environ)
self.servr._handle_request(
environ,
start_response_utils.null_start_response,
inst=self.inst,
request_type=instance.SHUTDOWN_REQUEST).WithSideEffects(advance_time)
self.servr._quit_event.wait(20)
self.inst.quit(force=True)
self.mox.ReplayAll()
self.servr._shutdown_instance(self.inst, 9000)
self.mox.VerifyAll()
class TestAutoScalingModuleWarmup(unittest.TestCase):
"""Tests for module.AutoScalingModule._warmup."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.mox.StubOutWithMock(module.Module, 'build_request_environ')
def tearDown(self):
self.mox.UnsetStubs()
def test_warmup(self):
s = AutoScalingModuleFacade(balanced_port=8080)
self.mox.StubOutWithMock(s, '_handle_request')
self.mox.StubOutWithMock(s._condition, 'notify')
inst = self.mox.CreateMock(instance.Instance)
environ = object()
s.build_request_environ('GET', '/_ah/warmup', [], '', '0.1.0.3', 8080,
fake_login=True).AndReturn(environ)
s._handle_request(environ,
mox.IgnoreArg(),
inst=inst,
request_type=instance.READY_REQUEST)
s._condition.notify(1)
self.mox.ReplayAll()
s._warmup(inst)
self.mox.VerifyAll()
class TestAutoScalingModuleAddInstance(unittest.TestCase):
"""Tests for module.AutoScalingModule._add_instance."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.factory = self.mox.CreateMock(instance.InstanceFactory)
self.factory.max_concurrent_requests = 10
def tearDown(self):
self.mox.UnsetStubs()
def test_permit_warmup(self):
s = AutoScalingModuleFacade(instance_factory=self.factory)
self.mox.StubOutWithMock(s, '_async_warmup')
self.mox.StubOutWithMock(s._condition, 'notify')
inst = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(mox.Regex('[a-f0-9]{36}'),
expect_ready_request=True).AndReturn(inst)
inst.start().AndReturn(True)
s._async_warmup(inst)
self.mox.ReplayAll()
self.assertEqual(inst, s._add_instance(permit_warmup=True))
self.mox.VerifyAll()
self.assertEqual(1, len(s._instances))
def test_no_permit_warmup(self):
s = AutoScalingModuleFacade(instance_factory=self.factory)
self.mox.StubOutWithMock(s._condition, 'notify')
inst = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(mox.Regex('[a-f0-9]{36}'),
expect_ready_request=False).AndReturn(inst)
inst.start().AndReturn(True)
s._condition.notify(10)
self.mox.ReplayAll()
self.assertEqual(inst, s._add_instance(permit_warmup=False))
self.mox.VerifyAll()
self.assertIn(inst, s._instances)
def test_failed_to_start(self):
s = AutoScalingModuleFacade(instance_factory=self.factory)
self.mox.StubOutWithMock(s, '_async_warmup')
self.mox.StubOutWithMock(s._condition, 'notify')
inst = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(mox.Regex('[a-f0-9]{36}'),
expect_ready_request=True).AndReturn(inst)
inst.start().AndReturn(False)
self.mox.ReplayAll()
self.assertIsNone(s._add_instance(permit_warmup=True))
self.mox.VerifyAll()
self.assertEqual(1, len(s._instances))
def test_max_instances(self):
s = AutoScalingModuleFacade(instance_factory=self.factory,
max_instances=1)
self.mox.StubOutWithMock(s._condition, 'notify')
inst = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(mox.Regex('[a-f0-9]{36}'),
expect_ready_request=False).AndReturn(inst)
inst.start().AndReturn(True)
s._condition.notify(10)
self.mox.ReplayAll()
self.assertEqual(inst, s._add_instance(permit_warmup=False))
self.assertEqual(None, s._add_instance(permit_warmup=False))
self.mox.VerifyAll()
self.assertEqual(1, len(s._instances))
class TestAutoScalingInstancePoolHandleScriptRequest(unittest.TestCase):
"""Tests for module.AutoScalingModule.handle."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.inst = self.mox.CreateMock(instance.Instance)
self.environ = {}
self.start_response = object()
self.response = [object()]
self.url_map = object()
self.match = object()
self.request_id = object()
self.auto_module = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.auto_module, '_choose_instance')
self.mox.StubOutWithMock(self.auto_module, '_add_instance')
self.mox.stubs.Set(time, 'time', lambda: 0.0)
def tearDown(self):
self.mox.UnsetStubs()
def test_handle_script_request(self):
self.auto_module._choose_instance(0.1).AndReturn(self.inst)
self.inst.handle(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.NORMAL_REQUEST).AndReturn(self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.auto_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
self.assertEqual([(mox.IgnoreArg(), 1)],
list(self.auto_module._outstanding_request_history))
def test_handle_cannot_accept_request(self):
self.auto_module._choose_instance(0.1).AndReturn(self.inst)
self.auto_module._choose_instance(0.1).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.auto_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
self.assertEqual([(mox.IgnoreArg(), 1)],
list(self.auto_module._outstanding_request_history))
def test_handle_new_instance(self):
self.auto_module._choose_instance(0.1).AndReturn(None)
self.auto_module._add_instance(permit_warmup=False).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.auto_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_new_instance_none_returned(self):
self.auto_module._choose_instance(0.1).AndReturn(None)
self.auto_module._add_instance(permit_warmup=False).AndReturn(None)
self.auto_module._choose_instance(0.2).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.auto_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
class TestAutoScalingInstancePoolTrimRequestTimesAndOutstanding(
unittest.TestCase):
"""Tests for AutoScalingModule._trim_outstanding_request_history."""
def setUp(self):
api_server.test_setup_stubs()
def test_trim_outstanding_request_history(self):
servr = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
servr._outstanding_request_history.append((0, 100))
servr._outstanding_request_history.append((1.0, 101))
servr._outstanding_request_history.append((1.2, 102))
servr._outstanding_request_history.append((2.5, 103))
now = time.time()
servr._outstanding_request_history.append((now, 42))
servr._outstanding_request_history.append((now + 1, 43))
servr._outstanding_request_history.append((now + 3, 44))
servr._outstanding_request_history.append((now + 4, 45))
servr._trim_outstanding_request_history()
self.assertEqual([(now, 42), (now + 1, 43), (now + 3, 44), (now + 4, 45)],
list(servr._outstanding_request_history))
class TestAutoScalingInstancePoolGetNumRequiredInstances(unittest.TestCase):
"""Tests for AutoScalingModule._outstanding_request_history."""
def setUp(self):
api_server.test_setup_stubs()
self.servr = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 5))
def test_get_num_required_instances(self):
now = time.time()
self.servr._outstanding_request_history.append((now, 42))
self.servr._outstanding_request_history.append((now + 1, 43))
self.servr._outstanding_request_history.append((now + 3, 44))
self.servr._outstanding_request_history.append((now + 4, 45))
self.assertEqual(9, self.servr._get_num_required_instances())
def test_no_requests(self):
self.assertEqual(0, self.servr._get_num_required_instances())
class TestAutoScalingInstancePoolSplitInstances(unittest.TestCase):
"""Tests for module.AutoScalingModule._split_instances."""
class Instance(object):
def __init__(self, num_outstanding_requests, can_accept_requests=True):
self.num_outstanding_requests = num_outstanding_requests
self.can_accept_requests = can_accept_requests
def __repr__(self):
return str(self.num_outstanding_requests)
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.servr = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.servr, '_get_num_required_instances')
def tearDown(self):
self.mox.UnsetStubs()
def test_split_instances(self):
instance1 = self.Instance(1)
instance2 = self.Instance(2, can_accept_requests=False)
instance3 = self.Instance(3)
instance4 = self.Instance(4)
instance5 = self.Instance(5)
instance6 = self.Instance(6)
instance7 = self.Instance(7)
instance8 = self.Instance(8, can_accept_requests=False)
instance9 = self.Instance(9)
instance10 = self.Instance(10)
self.servr._get_num_required_instances().AndReturn(5)
self.servr._instances = set([instance1, instance2, instance3, instance4,
instance5, instance6, instance7, instance8,
instance9, instance10])
self.mox.ReplayAll()
self.assertEqual(
(set([instance10, instance9, instance7,
instance6, instance5]),
set([instance1, instance2, instance3, instance4, instance8])),
self.servr._split_instances())
self.mox.VerifyAll()
def test_split_instances_no_instances(self):
self.servr._get_num_required_instances().AndReturn(5)
self.servr._instances = set([])
self.mox.ReplayAll()
self.assertEqual((set([]), set([])),
self.servr._split_instances())
self.mox.VerifyAll()
def test_split_instances_no_instances_not_enough_accepting_requests(self):
instance1 = self.Instance(1)
instance2 = self.Instance(1, can_accept_requests=False)
instance3 = self.Instance(2, can_accept_requests=False)
self.servr._get_num_required_instances().AndReturn(5)
self.servr._instances = set([instance1, instance2, instance3])
self.mox.ReplayAll()
self.assertEqual((set([instance1]), set([instance2, instance3])),
self.servr._split_instances())
self.mox.VerifyAll()
def test_split_instances_no_required_instances(self):
instance1 = self.Instance(1)
instance2 = self.Instance(2, can_accept_requests=False)
instance3 = self.Instance(3, can_accept_requests=False)
instance4 = self.Instance(4)
instance5 = self.Instance(5)
instance6 = self.Instance(6)
instance7 = self.Instance(7)
instance8 = self.Instance(8)
self.servr._get_num_required_instances().AndReturn(0)
self.servr._instances = set([instance1, instance2, instance3, instance4,
instance5, instance6, instance7, instance8])
self.mox.ReplayAll()
self.assertEqual(
(set(),
set([instance8, instance7, instance6, instance5, instance4,
instance3, instance2, instance1])),
self.servr._split_instances())
self.mox.VerifyAll()
class TestAutoScalingInstancePoolChooseInstances(unittest.TestCase):
"""Tests for module.AutoScalingModule._choose_instance."""
class Instance(object):
def __init__(self, num_outstanding_requests, can_accept_requests=True):
self.num_outstanding_requests = num_outstanding_requests
self.remaining_request_capacity = 10 - num_outstanding_requests
self.can_accept_requests = can_accept_requests
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.servr = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.servr, '_split_instances')
self.mox.StubOutWithMock(self.servr._condition, 'wait')
self.time = 10
self.mox.stubs.Set(time, 'time', lambda: self.time)
def advance_time(self, *unused_args):
self.time += 10
def tearDown(self):
self.mox.UnsetStubs()
def test_choose_instance_required_available(self):
instance1 = self.Instance(1)
instance2 = self.Instance(2)
instance3 = self.Instance(3)
instance4 = self.Instance(4)
self.servr._split_instances().AndReturn((set([instance3, instance4]),
set([instance1, instance2])))
self.mox.ReplayAll()
self.assertEqual(instance3, # Least busy required instance.
self.servr._choose_instance(15))
self.mox.VerifyAll()
def test_choose_instance_no_instances(self):
self.servr._split_instances().AndReturn((set([]), set([])))
self.servr._condition.wait(5).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(15))
self.mox.VerifyAll()
def test_choose_instance_no_instance_that_can_accept_requests(self):
instance1 = self.Instance(1, can_accept_requests=False)
self.servr._split_instances().AndReturn((set([]), set([instance1])))
self.servr._condition.wait(5).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(15))
self.mox.VerifyAll()
def test_choose_instance_required_full(self):
instance1 = self.Instance(1)
instance2 = self.Instance(2)
instance3 = self.Instance(10)
instance4 = self.Instance(10)
self.servr._split_instances().AndReturn((set([instance3, instance4]),
set([instance1, instance2])))
self.mox.ReplayAll()
self.assertEqual(instance2, # Busyest non-required instance.
self.servr._choose_instance(15))
self.mox.VerifyAll()
def test_choose_instance_must_wait(self):
instance1 = self.Instance(10)
instance2 = self.Instance(10)
self.servr._split_instances().AndReturn((set([instance1]),
set([instance2])))
self.servr._condition.wait(5).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertIsNone(self.servr._choose_instance(15))
self.mox.VerifyAll()
class TestAutoScalingInstancePoolAdjustInstances(unittest.TestCase):
"""Tests for module.AutoScalingModule._adjust_instances."""
class Instance(object):
def __init__(self, num_outstanding_requests):
self.num_outstanding_requests = num_outstanding_requests
def quit(self):
pass
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.servr = AutoScalingModuleFacade(
module_configuration=ModuleConfigurationStub(
automatic_scaling=appinfo.AutomaticScaling(
min_pending_latency='0.1s',
max_pending_latency='1.0s',
min_idle_instances=1,
max_idle_instances=2)),
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.servr, '_split_instances')
self.mox.StubOutWithMock(self.servr, '_add_instance')
def tearDown(self):
self.mox.UnsetStubs()
def test_adjust_instances_create_new(self):
instance1 = self.Instance(0)
instance2 = self.Instance(2)
instance3 = self.Instance(3)
instance4 = self.Instance(4)
self.servr._instances = set([instance1, instance2, instance3, instance4])
self.servr._split_instances().AndReturn(
(set([instance1, instance2, instance3, instance4]),
set([])))
self.servr._add_instance(permit_warmup=True)
self.mox.ReplayAll()
self.servr._adjust_instances()
self.mox.VerifyAll()
def test_adjust_instances_quit_idle(self):
instance1 = self.Instance(0)
instance2 = self.Instance(2)
instance3 = self.Instance(3)
instance4 = self.Instance(4)
self.mox.StubOutWithMock(instance1, 'quit')
self.servr._instances = set([instance1, instance2, instance3, instance4])
self.servr._split_instances().AndReturn(
(set([]),
set([instance1, instance2, instance3, instance4])))
instance1.quit()
self.mox.ReplayAll()
self.servr._adjust_instances()
self.mox.VerifyAll()
def test_adjust_instances_quit_idle_with_race(self):
instance1 = self.Instance(0)
instance2 = self.Instance(2)
instance3 = self.Instance(3)
instance4 = self.Instance(4)
self.mox.StubOutWithMock(instance1, 'quit')
self.servr._instances = set([instance1, instance2, instance3, instance4])
self.servr._split_instances().AndReturn(
(set([]),
set([instance1, instance2, instance3, instance4])))
instance1.quit().AndRaise(instance.CannotQuitServingInstance)
self.mox.ReplayAll()
self.servr._adjust_instances()
self.mox.VerifyAll()
class TestAutoScalingInstancePoolHandleChanges(unittest.TestCase):
"""Tests for module.AutoScalingModule._handle_changes."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.instance_factory = instance.InstanceFactory(object(), 10)
self.servr = AutoScalingModuleFacade(
instance_factory=self.instance_factory)
self.mox.StubOutWithMock(self.instance_factory, 'files_changed')
self.mox.StubOutWithMock(self.instance_factory, 'configuration_changed')
self.mox.StubOutWithMock(self.servr, '_maybe_restart_instances')
self.mox.StubOutWithMock(self.servr, '_create_url_handlers')
self.mox.StubOutWithMock(self.servr._module_configuration,
'check_for_updates')
self.mox.StubOutWithMock(self.servr._watcher, 'has_changes')
def tearDown(self):
self.mox.UnsetStubs()
def test_no_changes(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.servr._maybe_restart_instances(config_changed=False,
file_changed=False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_irrelevant_config_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.servr._maybe_restart_instances(config_changed=False,
file_changed=False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_restart_config_change(self):
conf_change = frozenset([application_configuration.ENV_VARIABLES_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.instance_factory.configuration_changed(conf_change)
self.servr._maybe_restart_instances(config_changed=True, file_changed=False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_handler_change(self):
conf_change = frozenset([application_configuration.HANDLERS_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.servr._create_url_handlers()
self.instance_factory.configuration_changed(conf_change)
self.servr._maybe_restart_instances(config_changed=True, file_changed=False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_file_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(True)
self.instance_factory.files_changed()
self.servr._maybe_restart_instances(config_changed=False, file_changed=True)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
class TestAutoScalingInstancePoolMaybeRestartInstances(unittest.TestCase):
"""Tests for module.AutoScalingModule._maybe_restart_instances."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.instance_factory = instance.InstanceFactory(object(), 10)
self.instance_factory.FILE_CHANGE_INSTANCE_RESTART_POLICY = instance.ALWAYS
self.servr = AutoScalingModuleFacade(instance_factory=self.instance_factory)
self.inst1 = self.mox.CreateMock(instance.Instance)
self.inst2 = self.mox.CreateMock(instance.Instance)
self.inst3 = self.mox.CreateMock(instance.Instance)
self.inst1.total_requests = 2
self.inst2.total_requests = 0
self.inst3.total_requests = 4
self.servr._instances.add(self.inst1)
self.servr._instances.add(self.inst2)
self.servr._instances.add(self.inst3)
def tearDown(self):
self.mox.UnsetStubs()
def test_no_changes(self):
self.mox.ReplayAll()
self.servr._maybe_restart_instances(config_changed=False,
file_changed=False)
self.mox.VerifyAll()
def test_config_change(self):
self.inst1.quit(allow_async=True).InAnyOrder()
self.inst2.quit(allow_async=True).InAnyOrder()
self.inst3.quit(allow_async=True).InAnyOrder()
self.mox.ReplayAll()
self.servr._maybe_restart_instances(config_changed=True,
file_changed=False)
self.mox.VerifyAll()
def test_file_change_restart_always(self):
self.instance_factory.FILE_CHANGE_INSTANCE_RESTART_POLICY = instance.ALWAYS
self.inst1.quit(allow_async=True).InAnyOrder()
self.inst2.quit(allow_async=True).InAnyOrder()
self.inst3.quit(allow_async=True).InAnyOrder()
self.mox.ReplayAll()
self.servr._maybe_restart_instances(config_changed=False,
file_changed=True)
self.mox.VerifyAll()
self.assertSequenceEqual(set(), self.servr._instances)
def test_file_change_restart_after_first_request(self):
self.instance_factory.FILE_CHANGE_INSTANCE_RESTART_POLICY = (
instance.AFTER_FIRST_REQUEST)
self.inst1.quit(allow_async=True).InAnyOrder()
self.inst3.quit(allow_async=True).InAnyOrder()
self.mox.ReplayAll()
self.servr._maybe_restart_instances(config_changed=False,
file_changed=True)
self.mox.VerifyAll()
self.assertSequenceEqual(set([self.inst2]), self.servr._instances)
def test_file_change_restart_never(self):
self.instance_factory.FILE_CHANGE_INSTANCE_RESTART_POLICY = instance.NEVER
self.mox.ReplayAll()
self.servr._maybe_restart_instances(config_changed=False,
file_changed=True)
self.mox.VerifyAll()
self.assertSequenceEqual(set([self.inst1, self.inst2, self.inst3]),
self.servr._instances)
class TestAutoScalingInstancePoolLoopAdjustingInstances(unittest.TestCase):
"""Tests for module.AutoScalingModule._adjust_instances."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.servr = AutoScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
def tearDown(self):
self.mox.UnsetStubs()
def test_loop_and_quit(self):
self.mox.StubOutWithMock(self.servr, '_adjust_instances')
self.mox.StubOutWithMock(self.servr, '_handle_changes')
inst1 = self.mox.CreateMock(instance.Instance)
inst2 = self.mox.CreateMock(instance.Instance)
inst3 = self.mox.CreateMock(instance.Instance)
self.servr._instances.add(inst1)
self.servr._instances.add(inst2)
self.servr._instances.add(inst3)
self.servr._handle_changes()
def do_quit(*unused_args):
self.servr._quit_event.set()
self.servr._adjust_instances().WithSideEffects(do_quit)
self.mox.ReplayAll()
self.servr._loop_adjusting_instances()
self.mox.VerifyAll()
class TestAutoScalingInstancePoolAutomaticScaling(unittest.TestCase):
def setUp(self):
api_server.test_setup_stubs()
def _create_module(self, automatic_scaling):
return AutoScalingModuleFacade(
module_configuration=ModuleConfigurationStub(
automatic_scaling=automatic_scaling),
instance_factory=instance.InstanceFactory(object(), 10))
def test_unset_automatic_settings(self):
settings = appinfo.AutomaticScaling()
pool = self._create_module(settings)
self.assertEqual(0.1, pool._min_pending_latency)
self.assertEqual(0.5, pool._max_pending_latency)
self.assertEqual(1, pool._min_idle_instances)
self.assertEqual(1000, pool._max_idle_instances)
def test_automatic_automatic_settings(self):
settings = appinfo.AutomaticScaling(
min_pending_latency='automatic',
max_pending_latency='automatic',
min_idle_instances='automatic',
max_idle_instances='automatic')
pool = self._create_module(settings)
self.assertEqual(0.1, pool._min_pending_latency)
self.assertEqual(0.5, pool._max_pending_latency)
self.assertEqual(1, pool._min_idle_instances)
self.assertEqual(1000, pool._max_idle_instances)
def test_explicit_automatic_settings(self):
settings = appinfo.AutomaticScaling(
min_pending_latency='1234ms',
max_pending_latency='5.67s',
min_idle_instances='3',
max_idle_instances='20')
pool = self._create_module(settings)
self.assertEqual(1.234, pool._min_pending_latency)
self.assertEqual(5.67, pool._max_pending_latency)
self.assertEqual(3, pool._min_idle_instances)
self.assertEqual(20, pool._max_idle_instances)
class TestManualScalingModuleStart(unittest.TestCase):
"""Tests for module.ManualScalingModule._start_instance."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.mox.StubOutWithMock(module.Module, 'build_request_environ')
def tearDown(self):
self.mox.UnsetStubs()
def test_instance_start_success(self):
s = ManualScalingModuleFacade(balanced_port=8080)
self.mox.StubOutWithMock(s, '_handle_request')
self.mox.StubOutWithMock(s._condition, 'notify')
wsgi_servr = self.mox.CreateMock(wsgi_server.WsgiServer)
wsgi_servr.port = 12345
inst = self.mox.CreateMock(instance.Instance)
inst.instance_id = 0
inst.start().AndReturn(True)
environ = object()
s.build_request_environ('GET', '/_ah/start', [], '', '0.1.0.3', 12345,
fake_login=True).AndReturn(environ)
s._handle_request(environ,
mox.IgnoreArg(),
inst=inst,
request_type=instance.READY_REQUEST)
s._condition.notify(1)
self.mox.ReplayAll()
s._start_instance(wsgi_servr, inst)
self.mox.VerifyAll()
def test_instance_start_failure(self):
s = ManualScalingModuleFacade(balanced_port=8080)
self.mox.StubOutWithMock(s, '_handle_request')
self.mox.StubOutWithMock(s._condition, 'notify')
wsgi_servr = self.mox.CreateMock(wsgi_server.WsgiServer)
wsgi_servr.port = 12345
inst = self.mox.CreateMock(instance.Instance)
inst.instance_id = 0
inst.start().AndReturn(False)
self.mox.ReplayAll()
s._start_instance(wsgi_servr, inst)
self.mox.VerifyAll()
class TestManualScalingModuleAddInstance(unittest.TestCase):
"""Tests for module.ManualScalingModule._add_instance."""
class WsgiServer(object):
def __init__(self, port):
self.port = port
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.factory = self.mox.CreateMock(instance.InstanceFactory)
self.factory.max_concurrent_requests = 10
def tearDown(self):
self.mox.UnsetStubs()
def test_add_while_started(self):
servr = ManualScalingModuleFacade(instance_factory=self.factory)
inst = self.mox.CreateMock(instance.Instance)
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.mox.StubOutWithMock(wsgi_server.WsgiServer, 'start')
self.mox.StubOutWithMock(wsgi_server.WsgiServer, 'port')
wsgi_server.WsgiServer.port = 12345
self.factory.new_instance(0, expect_ready_request=True).AndReturn(inst)
wsgi_server.WsgiServer.start()
module._THREAD_POOL.submit(servr._start_instance,
mox.IsA(wsgi_server.WsgiServer), inst)
self.mox.ReplayAll()
servr._add_instance()
self.mox.VerifyAll()
self.assertIn(inst, servr._instances)
self.assertEqual((servr, inst), servr._port_registry.get(12345))
def test_add_while_stopped(self):
servr = ManualScalingModuleFacade(instance_factory=self.factory)
servr._suspended = True
inst = self.mox.CreateMock(instance.Instance)
self.mox.StubOutWithMock(wsgi_server.WsgiServer, 'start')
self.mox.StubOutWithMock(wsgi_server.WsgiServer, 'port')
wsgi_server.WsgiServer.port = 12345
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.factory.new_instance(0, expect_ready_request=True).AndReturn(inst)
wsgi_server.WsgiServer.start()
self.mox.ReplayAll()
servr._add_instance()
self.mox.VerifyAll()
self.assertIn(inst, servr._instances)
self.assertEqual((servr, inst), servr._port_registry.get(12345))
class TestManualScalingInstancePoolHandleScriptRequest(unittest.TestCase):
"""Tests for module.ManualScalingModule.handle."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.inst = self.mox.CreateMock(instance.Instance)
self.inst.instance_id = 0
self.environ = {}
self.start_response = object()
self.response = [object()]
self.url_map = object()
self.match = object()
self.request_id = object()
self.manual_module = ManualScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.manual_module, '_choose_instance')
self.mox.StubOutWithMock(self.manual_module, '_add_instance')
self.mox.StubOutWithMock(self.manual_module._condition, 'notify')
self.mox.stubs.Set(time, 'time', lambda: 0.0)
def tearDown(self):
self.mox.UnsetStubs()
def test_handle_script_request(self):
self.manual_module._choose_instance(10.0).AndReturn(self.inst)
self.inst.handle(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.NORMAL_REQUEST).AndReturn(self.response)
self.manual_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.manual_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_cannot_accept_request(self):
self.manual_module._choose_instance(10.0).AndReturn(self.inst)
self.manual_module._choose_instance(10.0).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.manual_module._condition.notify()
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.manual_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.manual_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_must_wait(self):
self.manual_module._choose_instance(10.0).AndReturn(None)
self.manual_module._choose_instance(10.0).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.manual_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.manual_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_timeout(self):
self.time = 0.0
def advance_time(*unused_args):
self.time += 11
self.mox.stubs.Set(time, 'time', lambda: self.time)
self.mox.StubOutWithMock(self.manual_module, '_error_response')
self.manual_module._choose_instance(10.0).WithSideEffects(advance_time)
self.manual_module._error_response(self.environ, self.start_response,
503).AndReturn(self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.manual_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
class TestManualScalingInstancePoolChooseInstances(unittest.TestCase):
"""Tests for module.ManualScalingModule._choose_instance."""
class Instance(object):
def __init__(self, can_accept_requests):
self.can_accept_requests = can_accept_requests
def setUp(self):
self.mox = mox.Mox()
api_server.test_setup_stubs()
self.servr = ManualScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.servr._condition, 'wait')
self.time = 0
self.mox.stubs.Set(time, 'time', lambda: self.time)
def advance_time(self, *unused_args):
self.time += 10
def tearDown(self):
self.mox.UnsetStubs()
def test_choose_instance_first_can_accept(self):
instance1 = self.Instance(True)
instance2 = self.Instance(True)
self.servr._instances = [instance1, instance2]
self.mox.ReplayAll()
self.assertEqual(instance1, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_first_cannot_accept(self):
instance1 = self.Instance(False)
instance2 = self.Instance(True)
self.servr._instances = [instance1, instance2]
self.mox.ReplayAll()
self.assertEqual(instance2, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_none_can_accept(self):
instance1 = self.Instance(False)
instance2 = self.Instance(False)
self.servr._instances = [instance1, instance2]
self.servr._condition.wait(5).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(5))
self.mox.VerifyAll()
def test_choose_instance_no_instances(self):
self.servr._condition.wait(5).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(5))
self.mox.VerifyAll()
class TestManualScalingInstancePoolSetNumInstances(unittest.TestCase):
"""Tests for module.ManualScalingModule.set_num_instances."""
def setUp(self):
self.mox = mox.Mox()
api_server.test_setup_stubs()
self.module = ManualScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self._instance = self.mox.CreateMock(instance.Instance)
self._wsgi_server = self.mox.CreateMock(wsgi_server.WsgiServer)
self._wsgi_server.port = 8080
self.module._instances = [self._instance]
self.module._wsgi_servers = [self._wsgi_server]
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.mox.StubOutWithMock(self.module, '_add_instance')
self.mox.StubOutWithMock(self.module, '_shutdown_instance')
def tearDown(self):
self.mox.UnsetStubs()
def test_no_op(self):
self.mox.ReplayAll()
self.assertEqual(1, self.module.get_num_instances())
self.module.set_num_instances(1)
self.mox.VerifyAll()
def test_add_an_instance(self):
self.module._add_instance()
self.mox.ReplayAll()
self.assertEqual(1, self.module.get_num_instances())
self.module.set_num_instances(2)
self.mox.VerifyAll()
def test_remove_an_instance(self):
module._THREAD_POOL.submit(self.module._quit_instance,
self._instance,
self._wsgi_server)
self._instance.quit(expect_shutdown=True)
self._wsgi_server.quit()
self.module._shutdown_instance(self._instance, 8080)
self.mox.ReplayAll()
self.assertEqual(1, self.module.get_num_instances())
self.module.set_num_instances(0)
self.module._quit_instance(self._instance,
self._wsgi_server)
self.mox.VerifyAll()
class TestManualScalingInstancePoolSuspendAndResume(unittest.TestCase):
"""Tests for module.ManualScalingModule.suspend and resume."""
def setUp(self):
self.mox = mox.Mox()
api_server.test_setup_stubs()
self.factory = self.mox.CreateMock(instance.InstanceFactory)
self.module = ManualScalingModuleFacade(
instance_factory=self.factory)
self._instance = self.mox.CreateMock(instance.Instance)
self._wsgi_server = wsgi_server.WsgiServer(('localhost', 0), None)
self.module._instances = [self._instance]
self.module._wsgi_servers = [self._wsgi_server]
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.mox.StubOutWithMock(self.module, '_shutdown_instance')
self._wsgi_server.start()
def tearDown(self):
self._wsgi_server.quit()
self.mox.UnsetStubs()
def test_already_suspended(self):
self.module._suspended = True
self.assertRaises(request_info.VersionAlreadyStoppedError,
self.module.suspend)
def test_already_resumed(self):
self.assertRaises(request_info.VersionAlreadyStartedError,
self.module.resume)
def test_suspend_instance(self):
module._THREAD_POOL.submit(self.module._suspend_instance, self._instance,
self._wsgi_server.port)
self._instance.quit(expect_shutdown=True)
port = object()
self.module._shutdown_instance(self._instance, port)
self.mox.ReplayAll()
self.module.suspend()
self.module._suspend_instance(self._instance, port)
self.mox.VerifyAll()
self.assertEqual(404, self._wsgi_server._error)
self.assertEqual(None, self._wsgi_server._app)
self.assertTrue(self.module._suspended)
def test_resume(self):
self.module._suspended = True
self.module._instances = [object()]
self.factory.new_instance(0, expect_ready_request=True).AndReturn(
self._instance)
module._THREAD_POOL.submit(self.module._start_instance, self._wsgi_server,
self._instance)
self.mox.ReplayAll()
self.module.resume()
self.mox.VerifyAll()
self.assertEqual(self.module._handle_request,
self._wsgi_server._app.func)
self.assertEqual({'inst': self._instance},
self._wsgi_server._app.keywords)
self.assertFalse(self.module._suspended)
def test_restart(self):
self._new_instance = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(0, expect_ready_request=True).AndReturn(
self._new_instance)
module._THREAD_POOL.submit(self.module._suspend_instance, self._instance,
self._wsgi_server.port)
module._THREAD_POOL.submit(self.module._start_instance, self._wsgi_server,
self._new_instance)
self._instance.quit(expect_shutdown=True)
port = object()
self.module._shutdown_instance(self._instance, port)
self.mox.ReplayAll()
self.module.restart()
self.module._suspend_instance(self._instance, port)
self.mox.VerifyAll()
self.assertEqual(self.module._handle_request,
self._wsgi_server._app.func)
self.assertEqual({'inst': self._new_instance},
self._wsgi_server._app.keywords)
self.assertFalse(self.module._suspended)
class TestManualScalingInstancePoolHandleChanges(unittest.TestCase):
"""Tests for module.ManualScalingModule._handle_changes."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.instance_factory = instance.InstanceFactory(object(), 10)
self.servr = ManualScalingModuleFacade(
instance_factory=self.instance_factory)
self.mox.StubOutWithMock(self.instance_factory, 'files_changed')
self.mox.StubOutWithMock(self.instance_factory, 'configuration_changed')
self.mox.StubOutWithMock(self.servr, 'restart')
self.mox.StubOutWithMock(self.servr, '_create_url_handlers')
self.mox.StubOutWithMock(self.servr._module_configuration,
'check_for_updates')
self.mox.StubOutWithMock(self.servr._watcher, 'has_changes')
def tearDown(self):
self.mox.UnsetStubs()
def test_no_changes(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_irrelevant_config_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_restart_config_change(self):
conf_change = frozenset([application_configuration.ENV_VARIABLES_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.instance_factory.configuration_changed(conf_change)
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_handler_change(self):
conf_change = frozenset([application_configuration.HANDLERS_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.servr._create_url_handlers()
self.instance_factory.configuration_changed(conf_change)
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_file_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(True)
self.instance_factory.files_changed()
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_restart_config_change_suspended(self):
self.servr._suspended = True
conf_change = frozenset([application_configuration.ENV_VARIABLES_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.instance_factory.configuration_changed(conf_change)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_handler_change_suspended(self):
self.servr._suspended = True
conf_change = frozenset([application_configuration.HANDLERS_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.servr._create_url_handlers()
self.instance_factory.configuration_changed(conf_change)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_file_change_suspended(self):
self.servr._suspended = True
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(True)
self.instance_factory.files_changed()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
class TestBasicScalingModuleStart(unittest.TestCase):
"""Tests for module.BasicScalingModule._start_instance."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.mox.StubOutWithMock(module.Module, 'build_request_environ')
def tearDown(self):
self.mox.UnsetStubs()
def test_instance_start_success(self):
s = BasicScalingModuleFacade(balanced_port=8080)
self.mox.StubOutWithMock(s, '_handle_request')
self.mox.StubOutWithMock(s._condition, 'notify')
wsgi_servr = self.mox.CreateMock(wsgi_server.WsgiServer)
wsgi_servr.port = 12345
s._wsgi_servers[0] = wsgi_servr
inst = self.mox.CreateMock(instance.Instance)
inst.instance_id = 0
s._instances[0] = inst
inst.start().AndReturn(True)
environ = object()
s.build_request_environ('GET', '/_ah/start', [], '', '0.1.0.3', 12345,
fake_login=True).AndReturn(environ)
s._handle_request(environ,
mox.IgnoreArg(),
inst=inst,
request_type=instance.READY_REQUEST)
s._condition.notify(1)
self.mox.ReplayAll()
s._start_instance(0)
self.mox.VerifyAll()
def test_instance_start_failure(self):
s = BasicScalingModuleFacade(balanced_port=8080)
self.mox.StubOutWithMock(s, '_handle_request')
self.mox.StubOutWithMock(s._condition, 'notify')
wsgi_servr = self.mox.CreateMock(wsgi_server.WsgiServer)
wsgi_servr.port = 12345
s._wsgi_servers[0] = wsgi_servr
inst = self.mox.CreateMock(instance.Instance)
inst.instance_id = 0
s._instances[0] = inst
inst.start().AndReturn(False)
self.mox.ReplayAll()
s._start_instance(0)
self.mox.VerifyAll()
def test_start_any_instance_success(self):
s = BasicScalingModuleFacade(balanced_port=8080)
s._instance_running = [True, False, False, True]
inst = object()
s._instances = [None, inst, None, None]
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
module._THREAD_POOL.submit(s._start_instance, 1)
self.mox.ReplayAll()
self.assertEqual(inst, s._start_any_instance())
self.mox.VerifyAll()
self.assertEqual([True, True, False, True], s._instance_running)
def test_start_any_instance_all_already_running(self):
s = BasicScalingModuleFacade(balanced_port=8080)
s._instance_running = [True, True, True, True]
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.mox.ReplayAll()
self.assertIsNone(s._start_any_instance())
self.mox.VerifyAll()
self.assertEqual([True, True, True, True], s._instance_running)
class TestBasicScalingInstancePoolHandleScriptRequest(unittest.TestCase):
"""Tests for module.BasicScalingModule.handle."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.inst = self.mox.CreateMock(instance.Instance)
self.inst.instance_id = 0
self.environ = {}
self.start_response = object()
self.response = [object()]
self.url_map = object()
self.match = object()
self.request_id = object()
self.basic_module = BasicScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.StubOutWithMock(self.basic_module, '_choose_instance')
self.mox.StubOutWithMock(self.basic_module, '_start_any_instance')
self.mox.StubOutWithMock(self.basic_module, '_start_instance')
self.mox.StubOutWithMock(self.basic_module._condition, 'wait')
self.mox.StubOutWithMock(self.basic_module._condition, 'notify')
self.time = 10
self.mox.stubs.Set(time, 'time', lambda: self.time)
def advance_time(self, *unused_args):
self.time += 11
def tearDown(self):
self.mox.UnsetStubs()
def test_handle_script_request(self):
self.basic_module._choose_instance(20).AndReturn(self.inst)
self.inst.handle(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.NORMAL_REQUEST).AndReturn(self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_cannot_accept_request(self):
self.basic_module._choose_instance(20).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.basic_module._condition.notify()
self.basic_module._choose_instance(20).AndReturn(self.inst)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_timeout(self):
self.mox.StubOutWithMock(self.basic_module, '_error_response')
self.basic_module._choose_instance(20).WithSideEffects(self.advance_time)
self.basic_module._error_response(self.environ, self.start_response,
503).AndReturn(self.response)
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_instance(self):
self.inst.instance_id = 0
self.inst.has_quit = False
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
inst=self.inst))
self.mox.VerifyAll()
def test_handle_instance_start_the_instance(self):
self.inst.instance_id = 0
self.inst.has_quit = False
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.basic_module._start_instance(0).AndReturn(True)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
inst=self.inst))
self.mox.VerifyAll()
def test_handle_instance_already_running(self):
self.inst.instance_id = 0
self.inst.has_quit = False
self.basic_module._instance_running[0] = True
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.inst.wait(20)
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndReturn(
self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
inst=self.inst))
self.mox.VerifyAll()
def test_handle_instance_timeout(self):
self.mox.StubOutWithMock(self.basic_module, '_error_response')
self.inst.instance_id = 0
self.inst.has_quit = False
self.basic_module._instance_running[0] = True
self.inst.handle(
self.environ, self.start_response, self.url_map, self.match,
self.request_id, instance.NORMAL_REQUEST).AndRaise(
instance.CannotAcceptRequests)
self.inst.wait(20).WithSideEffects(self.advance_time)
self.basic_module._error_response(self.environ, self.start_response,
503).AndReturn(self.response)
self.basic_module._condition.notify()
self.mox.ReplayAll()
self.assertEqual(
self.response,
self.basic_module._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
inst=self.inst))
self.mox.VerifyAll()
class TestBasicScalingInstancePoolChooseInstances(unittest.TestCase):
"""Tests for module.BasicScalingModule._choose_instance."""
class Instance(object):
def __init__(self, can_accept_requests):
self.can_accept_requests = can_accept_requests
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.servr = BasicScalingModuleFacade(
instance_factory=instance.InstanceFactory(object(), 10))
self.mox.stubs.Set(time, 'time', lambda: self.time)
self.mox.StubOutWithMock(self.servr._condition, 'wait')
self.mox.StubOutWithMock(self.servr, '_start_any_instance')
self.time = 0
def tearDown(self):
self.mox.UnsetStubs()
def advance_time(self, *unused_args):
self.time += 10
def test_choose_instance_first_can_accept(self):
instance1 = self.Instance(True)
instance2 = self.Instance(True)
self.servr._instances = [instance1, instance2]
self.mox.ReplayAll()
self.assertEqual(instance1, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_first_cannot_accept(self):
instance1 = self.Instance(False)
instance2 = self.Instance(True)
self.servr._instances = [instance1, instance2]
self.mox.ReplayAll()
self.assertEqual(instance2, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_none_can_accept(self):
instance1 = self.Instance(False)
instance2 = self.Instance(False)
self.servr._instance_running = [True, True]
self.servr._instances = [instance1, instance2]
self.servr._start_any_instance().AndReturn(None)
self.servr._condition.wait(1).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_start_an_instance(self):
instance1 = self.Instance(False)
instance2 = self.Instance(False)
mock_instance = self.mox.CreateMock(instance.Instance)
self.servr._instances = [instance1, instance2]
self.servr._instance_running = [True, False]
self.servr._start_any_instance().AndReturn(mock_instance)
mock_instance.wait(1)
self.mox.ReplayAll()
self.assertEqual(mock_instance, self.servr._choose_instance(1))
self.mox.VerifyAll()
def test_choose_instance_no_instances(self):
self.servr._start_any_instance().AndReturn(None)
self.servr._condition.wait(1).WithSideEffects(self.advance_time)
self.mox.ReplayAll()
self.assertEqual(None, self.servr._choose_instance(1))
self.mox.VerifyAll()
class TestBasicScalingInstancePoolInstanceManagement(unittest.TestCase):
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.factory = self.mox.CreateMock(instance.InstanceFactory)
self.factory.max_concurrent_requests = 10
self.mox.StubOutWithMock(module._THREAD_POOL, 'submit')
self.module = BasicScalingModuleFacade(instance_factory=self.factory,
host='localhost')
self.wsgi_server = self.module._wsgi_servers[0]
self.wsgi_server.start()
def tearDown(self):
self.wsgi_server.quit()
self.mox.UnsetStubs()
def test_restart(self):
old_instances = [self.mox.CreateMock(instance.Instance),
self.mox.CreateMock(instance.Instance)]
self.module._instances = old_instances[:]
self.module._instance_running = [True, False]
new_instance = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(0, expect_ready_request=True).AndReturn(
new_instance)
module._THREAD_POOL.submit(self.module._start_instance, 0)
old_instances[0].quit(expect_shutdown=True)
module._THREAD_POOL.submit(self.module._shutdown_instance, old_instances[0],
self.wsgi_server.port)
self.mox.ReplayAll()
self.module.restart()
self.mox.VerifyAll()
self.assertEqual([True, False], self.module._instance_running)
self.assertEqual(new_instance, self.module._instances[0])
self.assertEqual(self.module._handle_request,
self.module._wsgi_servers[0]._app.func)
self.assertEqual({'inst': new_instance},
self.module._wsgi_servers[0]._app.keywords)
def test_shutdown_idle_instances(self):
s = BasicScalingModuleFacade(instance_factory=self.factory)
old_instances = [self.mox.CreateMock(instance.Instance),
self.mox.CreateMock(instance.Instance),
self.mox.CreateMock(instance.Instance)]
self.module._instances = old_instances[:]
old_instances[0].idle_seconds = (self.module._instance_idle_timeout + 1)
old_instances[1].idle_seconds = 0
old_instances[2].idle_seconds = (self.module._instance_idle_timeout + 1)
self.module._instance_running = [True, True, False]
new_instance = self.mox.CreateMock(instance.Instance)
self.factory.new_instance(0, expect_ready_request=True).AndReturn(
new_instance)
old_instances[0].quit(expect_shutdown=True)
module._THREAD_POOL.submit(self.module._shutdown_instance, old_instances[0],
self.wsgi_server.port)
self.mox.ReplayAll()
self.module._shutdown_idle_instances()
self.mox.VerifyAll()
self.assertEqual([False, True, False], self.module._instance_running)
self.assertEqual(new_instance, self.module._instances[0])
self.assertEqual(self.module._handle_request,
self.module._wsgi_servers[0]._app.func)
self.assertEqual({'inst': new_instance},
self.module._wsgi_servers[0]._app.keywords)
class TestBasicScalingInstancePoolHandleChanges(unittest.TestCase):
"""Tests for module.BasicScalingModule._handle_changes."""
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.instance_factory = instance.InstanceFactory(object(), 10)
self.servr = BasicScalingModuleFacade(
instance_factory=self.instance_factory)
self.mox.StubOutWithMock(self.instance_factory, 'files_changed')
self.mox.StubOutWithMock(self.instance_factory, 'configuration_changed')
self.mox.StubOutWithMock(self.servr, 'restart')
self.mox.StubOutWithMock(self.servr, '_create_url_handlers')
self.mox.StubOutWithMock(self.servr._module_configuration,
'check_for_updates')
self.mox.StubOutWithMock(self.servr._watcher, 'has_changes')
def tearDown(self):
self.mox.UnsetStubs()
def test_no_changes(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_irrelevant_config_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(False)
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_restart_config_change(self):
conf_change = frozenset([application_configuration.ENV_VARIABLES_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.instance_factory.configuration_changed(conf_change)
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_handler_change(self):
conf_change = frozenset([application_configuration.HANDLERS_CHANGED])
self.servr._module_configuration.check_for_updates().AndReturn(conf_change)
self.servr._watcher.has_changes().AndReturn(False)
self.servr._create_url_handlers()
self.instance_factory.configuration_changed(conf_change)
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
def test_file_change(self):
self.servr._module_configuration.check_for_updates().AndReturn(frozenset())
self.servr._watcher.has_changes().AndReturn(True)
self.instance_factory.files_changed().AndReturn(True)
self.servr.restart()
self.mox.ReplayAll()
self.servr._handle_changes()
self.mox.VerifyAll()
class TestInteractiveCommandModule(unittest.TestCase):
def setUp(self):
api_server.test_setup_stubs()
self.mox = mox.Mox()
self.inst = self.mox.CreateMock(instance.Instance)
self.inst.instance_id = 0
self.environ = object()
self.start_response = object()
self.response = [object()]
self.url_map = object()
self.match = object()
self.request_id = object()
self.servr = module.InteractiveCommandModule(
ModuleConfigurationStub(),
'fakehost',
balanced_port=8000,
api_host='localhost',
api_port=9000,
auth_domain='gmail.com',
runtime_stderr_loglevel=1,
php_config=None,
python_config=None,
cloud_sql_config=None,
default_version_port=8080,
port_registry=dispatcher.PortRegistry(),
request_data=None,
dispatcher=None,
use_mtime_file_watcher=False,
allow_skipped_files=False,
threadsafe_override=None)
self.mox.StubOutWithMock(self.servr._instance_factory, 'new_instance')
self.mox.StubOutWithMock(self.servr, '_handle_request')
self.mox.StubOutWithMock(self.servr, 'build_request_environ')
def test_send_interactive_command(self):
def good_response(unused_environ, start_response, request_type):
start_response('200 OK', [])
return ['10\n']
environ = object()
self.servr.build_request_environ(
'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ)
self.servr._handle_request(
environ,
mox.IgnoreArg(),
request_type=instance.INTERACTIVE_REQUEST).WithSideEffects(
good_response)
self.mox.ReplayAll()
self.assertEqual('10\n', self.servr.send_interactive_command('print 5+5'))
self.mox.VerifyAll()
def test_send_interactive_command_handle_request_exception(self):
environ = object()
self.servr.build_request_environ(
'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ)
self.servr._handle_request(
environ,
mox.IgnoreArg(),
request_type=instance.INTERACTIVE_REQUEST).AndRaise(Exception('error'))
self.mox.ReplayAll()
self.assertRaisesRegexp(module.InteractiveCommandError,
'error',
self.servr.send_interactive_command,
'print 5+5')
self.mox.VerifyAll()
def test_send_interactive_command_handle_request_failure(self):
def good_response(unused_environ, start_response, request_type):
start_response('503 Service Unavailable', [])
return ['Instance was restarted while executing command']
environ = object()
self.servr.build_request_environ(
'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ)
self.servr._handle_request(
environ,
mox.IgnoreArg(),
request_type=instance.INTERACTIVE_REQUEST).WithSideEffects(
good_response)
self.mox.ReplayAll()
self.assertRaisesRegexp(module.InteractiveCommandError,
'Instance was restarted while executing command',
self.servr.send_interactive_command,
'print 5+5')
self.mox.VerifyAll()
def test_handle_script_request(self):
self.servr._instance_factory.new_instance(
mox.IgnoreArg(),
expect_ready_request=False).AndReturn(self.inst)
self.inst.start()
self.inst.handle(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.INTERACTIVE_REQUEST).AndReturn(['10\n'])
self.mox.ReplayAll()
self.assertEqual(
['10\n'],
self.servr._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_script_request_busy(self):
self.servr._instance_factory.new_instance(
mox.IgnoreArg(),
expect_ready_request=False).AndReturn(self.inst)
self.inst.start()
self.inst.handle(
self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.INTERACTIVE_REQUEST).AndRaise(instance.CannotAcceptRequests())
self.inst.wait(mox.IgnoreArg())
self.inst.handle(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.INTERACTIVE_REQUEST).AndReturn(['10\n'])
self.mox.ReplayAll()
self.assertEqual(
['10\n'],
self.servr._handle_script_request(self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
def test_handle_script_request_timeout(self):
self.servr._MAX_REQUEST_WAIT_TIME = 0
start_response = start_response_utils.CapturingStartResponse()
self.mox.ReplayAll()
self.assertEqual(
['The command timed-out while waiting for another one to complete'],
self.servr._handle_script_request(self.environ,
start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
self.assertEqual('503 Service Unavailable',
start_response.status)
def test_handle_script_request_restart(self):
def restart_and_raise(*args):
self.servr._inst = None
raise httplib.BadStatusLine('line')
start_response = start_response_utils.CapturingStartResponse()
self.servr._instance_factory.new_instance(
mox.IgnoreArg(),
expect_ready_request=False).AndReturn(self.inst)
self.inst.start()
self.inst.handle(
self.environ,
start_response,
self.url_map,
self.match,
self.request_id,
instance.INTERACTIVE_REQUEST).WithSideEffects(restart_and_raise)
self.mox.ReplayAll()
self.assertEqual(
['Instance was restarted while executing command'],
self.servr._handle_script_request(self.environ,
start_response,
self.url_map,
self.match,
self.request_id))
self.mox.VerifyAll()
self.assertEqual('503 Service Unavailable',
start_response.status)
def test_handle_script_request_unexpected_instance_exception(self):
self.servr._instance_factory.new_instance(
mox.IgnoreArg(),
expect_ready_request=False).AndReturn(self.inst)
self.inst.start()
self.inst.handle(
self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id,
instance.INTERACTIVE_REQUEST).AndRaise(httplib.BadStatusLine('line'))
self.mox.ReplayAll()
self.assertRaises(
httplib.BadStatusLine,
self.servr._handle_script_request,
self.environ,
self.start_response,
self.url_map,
self.match,
self.request_id)
self.mox.VerifyAll()
if __name__ == '__main__':
unittest.main()