def test_good_configuration(self):
        self.mox.StubOutWithMock(application_configuration,
                                 'ModuleConfiguration')
        static_backend_entry = backendinfo.BackendEntry(name='static')
        dynamic_backend_entry = backendinfo.BackendEntry(name='dynamic')
        backend_info = backendinfo.BackendInfoExternal(
            backends=[static_backend_entry, dynamic_backend_entry])
        module_config = object()
        application_configuration.ModuleConfiguration(
            '/appdir/app.yaml', None).AndReturn(module_config)
        application_configuration.BackendsConfiguration._parse_configuration(
            '/appdir/backends.yaml').AndReturn(backend_info)
        static_configuration = object()
        dynamic_configuration = object()
        application_configuration.BackendConfiguration(
            module_config, mox.IgnoreArg(),
            static_backend_entry).InAnyOrder().AndReturn(static_configuration)
        application_configuration.BackendConfiguration(
            module_config, mox.IgnoreArg(),
            dynamic_backend_entry).InAnyOrder().AndReturn(
                dynamic_configuration)

        self.mox.ReplayAll()
        config = application_configuration.BackendsConfiguration(
            '/appdir/app.yaml', '/appdir/backends.yaml')
        self.assertItemsEqual([static_configuration, dynamic_configuration],
                              config.get_backend_configurations())
        self.mox.VerifyAll()
    def test_check_for_changes(self):
        static_backend_entry = backendinfo.BackendEntry(name='static')
        dynamic_backend_entry = backendinfo.BackendEntry(name='dynamic')
        backend_info = backendinfo.BackendInfoExternal(
            backends=[static_backend_entry, dynamic_backend_entry])
        module_config = self.mox.CreateMock(
            application_configuration.ModuleConfiguration)
        self.mox.StubOutWithMock(application_configuration,
                                 'ModuleConfiguration')
        application_configuration.ModuleConfiguration(
            '/appdir/app.yaml', None).AndReturn(module_config)
        application_configuration.BackendsConfiguration._parse_configuration(
            '/appdir/backends.yaml').AndReturn(backend_info)
        module_config.check_for_updates().AndReturn(set())
        module_config.check_for_updates().AndReturn(set([1]))
        module_config.check_for_updates().AndReturn(set([2]))
        module_config.check_for_updates().AndReturn(set())

        self.mox.ReplayAll()
        config = application_configuration.BackendsConfiguration(
            '/appdir/app.yaml', '/appdir/backends.yaml')
        self.assertEqual(set(), config.check_for_updates('dynamic'))
        self.assertEqual(set([1]), config.check_for_updates('static'))
        self.assertEqual(set([1, 2]), config.check_for_updates('dynamic'))
        self.assertEqual(set([2]), config.check_for_updates('static'))
        self.mox.VerifyAll()
    def testCheckInitialized(self):
        entry = backendinfo.BackendEntry(name='foo')
        entry.CheckInitialized()

        entry = backendinfo.BackendEntry()
        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.CheckInitialized, entry)
    def testClass(self):
        entry = backendinfo.BackendEntry(name='1')

        entry.set_class('B1')
        self.assertEqual('B1', entry.get_class())
        entry.set_class('B2')
        self.assertEqual('B2', entry.get_class())
        entry.set_class('B4')
        self.assertEqual('B4', entry.get_class())
        entry.set_class('B8')
        self.assertEqual('B8', entry.get_class())

        entry.set_class('b1')
        self.assertEqual('b1', entry.get_class())
        entry.set_class('b2')
        self.assertEqual('b2', entry.get_class())
        entry.set_class('b4')
        self.assertEqual('b4', entry.get_class())
        entry.set_class('b8')
        self.assertEqual('b8', entry.get_class())

        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.set_class, entry, 'B0')
        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.set_class, entry, 'B3')
        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.set_class, entry, 'B5')
        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.set_class, entry, 'B7')
        self.assertRaises(validation.ValidationError,
                          backendinfo.BackendEntry.set_class, entry, 'B9')
    def testOptions(self):
        entry = backendinfo.BackendEntry(name='foo')
        self.assertFalse(entry.public)
        self.assertFalse(entry.dynamic)
        self.assertFalse(entry.failfast)
        self.assertEqual(None, entry.options)

        entry = backendinfo.BackendEntry(name='foo', options='public')
        self.assertTrue(entry.public)
        self.assertFalse(entry.failfast)
        self.assertEqual('public', entry.options)

        entry = backendinfo.BackendEntry(name='foo', options='dynamic')
        self.assertFalse(entry.public)
        self.assertTrue(entry.dynamic)
        self.assertFalse(entry.failfast)
        self.assertEqual('dynamic', entry.options)

        entry = backendinfo.BackendEntry(name='foo', options='failfast')
        self.assertFalse(entry.public)
        self.assertFalse(entry.dynamic)
        self.assertTrue(entry.failfast)
        self.assertEqual('failfast', entry.options)

        entry = backendinfo.BackendEntry(name='foo',
                                         options='public, dynamic, failfast')
        self.assertTrue(entry.public)
        self.assertTrue(entry.dynamic)
        self.assertTrue(entry.failfast)
        self.assertEqual('public, dynamic, failfast', entry.options)

        try:
            backendinfo.BackendEntry(name='foo', public=True)
            fail()
        except backendinfo.BadConfig:
            pass
        try:
            backendinfo.BackendEntry(name='foo', dynamic=True)
            fail()
        except backendinfo.BadConfig:
            pass
        try:
            backendinfo.BackendEntry(name='foo', failfast=True)
            fail()
        except backendinfo.BadConfig:
            pass

        try:
            backendinfo.BackendEntry(name='foo', options='hello')
            fail()
        except backendinfo.BadConfig:
            pass
    def test_good_configuration_dynamic_scaling(self):
        automatic_scaling = appinfo.AutomaticScaling(
            min_pending_latency='1.0s',
            max_pending_latency='2.0s',
            min_idle_instances=1,
            max_idle_instances=2)
        error_handlers = [appinfo.ErrorHandlers(file='error.html')]
        handlers = [appinfo.URLMap()]
        env_variables = appinfo.EnvironmentVariables()
        info = appinfo.AppInfoExternal(
            application='app',
            module='module1',
            version='1',
            runtime='python27',
            threadsafe=False,
            automatic_scaling=automatic_scaling,
            skip_files=r'\*.gif',
            error_handlers=error_handlers,
            handlers=handlers,
            inbound_services=['warmup'],
            env_variables=env_variables,
        )
        backend_entry = backendinfo.BackendEntry(name='dynamic',
                                                 instances='3',
                                                 options='public, dynamic',
                                                 start='handler')

        application_configuration.ModuleConfiguration._parse_configuration(
            '/appdir/app.yaml').AndReturn((info, []))
        os.path.getmtime('/appdir/app.yaml').AndReturn(10)

        self.mox.ReplayAll()
        module_config = application_configuration.ModuleConfiguration(
            '/appdir/app.yaml')
        config = application_configuration.BackendConfiguration(
            module_config, None, backend_entry)
        self.mox.VerifyAll()

        self.assertEqual(os.path.realpath('/appdir'), config.application_root)
        self.assertEqual('app', config.application)
        self.assertEqual('dynamic', config.module_name)
        self.assertEqual('1', config.major_version)
        self.assertRegexpMatches(config.version_id, r'dynamic:1\.\d+')
        self.assertEqual('python27', config.runtime)
        self.assertFalse(config.threadsafe)
        self.assertEqual(None, config.automatic_scaling)
        self.assertEqual(None, config.manual_scaling)
        self.assertEqual(appinfo.BasicScaling(max_instances='3'),
                         config.basic_scaling)
        self.assertEqual(info.GetNormalizedLibraries(),
                         config.normalized_libraries)
        self.assertEqual(r'\*.gif', config.skip_files)
        self.assertEqual(error_handlers, config.error_handlers)
        start_handler = appinfo.URLMap(url='/_ah/start',
                                       script=backend_entry.start,
                                       login='******')
        self.assertEqual([start_handler] + handlers, config.handlers)
        self.assertEqual(['warmup'], config.inbound_services)
        self.assertEqual(env_variables, config.env_variables)
    def testStart(self):
        entry = backendinfo.BackendEntry(name='1', start='bar/foo.py')
        self.assertEqual('bar/foo.py', entry.start)

        entry.start = 'a' * 256
        self.assertEqual('a' * 256, entry.start)
        try:
            entry.name = 'a' * 257
            self.fail()
        except validation.ValidationError:
            pass
    def testName(self):
        entry = backendinfo.BackendEntry(name='1')
        self.assertEqual('1', entry.name)

        entry.name = '1' * 100
        self.assertEqual('1' * 100, entry.name)
        try:
            entry.name = '1' * 101
            self.fail()
        except validation.ValidationError:
            pass
    def testToYAML(self):

        backend = backendinfo.BackendEntry(
            name='s1',
            instances=2,
            start='test/foo.py',
            options='public, dynamic, failfast',
            max_concurrent_requests=5,
            state='START',
        )
        backend.set_class('B2')
        self.assertEqual(
            'name: s1\n'
            'class: B2\n'
            'instances: 2\n'
            'start: test/foo.py\n'
            'options: public, dynamic, failfast\n'
            'max_concurrent_requests: 5\n'
            'state: START\n', backend.ToYAML())
        backend.set_class(None)
        self.assertEqual(
            'name: s1\n'
            'instances: 2\n'
            'start: test/foo.py\n'
            'options: public, dynamic, failfast\n'
            'max_concurrent_requests: 5\n'
            'state: START\n', backend.ToYAML())
        backend.instances = None
        self.assertEqual(
            'name: s1\n'
            'start: test/foo.py\n'
            'options: public, dynamic, failfast\n'
            'max_concurrent_requests: 5\n'
            'state: START\n', backend.ToYAML())
        backend.start = None
        self.assertEqual(
            'name: s1\n'
            'options: public, dynamic, failfast\n'
            'max_concurrent_requests: 5\n'
            'state: START\n', backend.ToYAML())
        backend.options = None
        self.assertEqual(
            'name: s1\n'
            'max_concurrent_requests: 5\n'
            'state: START\n', backend.ToYAML())
        backend.max_concurrent_requests = None
        self.assertEqual('name: s1\n' 'state: START\n', backend.ToYAML())
        backend.state = None
        self.assertEqual('name: s1\n', backend.ToYAML())
 def test_check_for_changes(self):
     backends_config = self.mox.CreateMock(
         application_configuration.BackendsConfiguration)
     config = application_configuration.BackendConfiguration(
         None, backends_config, backendinfo.BackendEntry(name='backend'))
     changes = object()
     backends_config.check_for_updates('backend').AndReturn([])
     backends_config.check_for_updates('backend').AndReturn(changes)
     minor_version = config._minor_version_id
     self.mox.ReplayAll()
     self.assertEqual([], config.check_for_updates())
     self.assertEqual(minor_version, config._minor_version_id)
     self.assertEqual(changes, config.check_for_updates())
     self.assertNotEqual(minor_version, config._minor_version_id)
     self.mox.VerifyAll()
  def test_vm_app_yaml_configuration(self):
    automatic_scaling = appinfo.AutomaticScaling(min_pending_latency='1.0s',
                                                 max_pending_latency='2.0s',
                                                 min_idle_instances=1,
                                                 max_idle_instances=2)
    vm_settings = appinfo.VmSettings()
    vm_settings['vm_runtime'] = 'myawesomeruntime'
    info = appinfo.AppInfoExternal(
        application='app',
        module='module1',
        version='1',
        runtime='vm',
        vm_settings=vm_settings,
        threadsafe=False,
        automatic_scaling=automatic_scaling,
    )
    backend_entry = backendinfo.BackendEntry(
        name='static',
        instances='3',
        options='public')
    application_configuration.ModuleConfiguration._parse_configuration(
        '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml']))
    os.path.getmtime('/appdir/app.yaml').AndReturn(10)

    self.mox.ReplayAll()
    module_config = application_configuration.ModuleConfiguration(
        '/appdir/app.yaml')
    config = application_configuration.BackendConfiguration(
        module_config, None, backend_entry)

    self.mox.VerifyAll()
    self.assertEqual(os.path.realpath('/appdir'), config.application_root)
    self.assertEqual('dev~app', config.application)
    self.assertEqual('app', config.application_external_name)
    self.assertEqual('dev', config.partition)
    self.assertEqual('static', config.module_name)
    self.assertEqual('1', config.major_version)
    self.assertRegexpMatches(config.version_id, r'static:1\.\d+')
    self.assertEqual('vm', config.runtime)
    self.assertEqual(vm_settings['vm_runtime'], config.effective_runtime)
    self.assertFalse(config.threadsafe)
    # Resident backends are assigned manual scaling.
    self.assertEqual(None, config.automatic_scaling)
    self.assertEqual(None, config.basic_scaling)
    self.assertEqual(appinfo.ManualScaling(instances='3'),
                     config.manual_scaling)
    def test_good_configuration(self):
        automatic_scaling = appinfo.AutomaticScaling(
            min_pending_latency='1.0s',
            max_pending_latency='2.0s',
            min_idle_instances=1,
            max_idle_instances=2)
        error_handlers = [appinfo.ErrorHandlers(file='error.html')]
        handlers = [appinfo.URLMap()]
        env_variables = appinfo.EnvironmentVariables()
        info = appinfo.AppInfoExternal(
            application='app',
            module='module1',
            version='1',
            runtime='python27',
            threadsafe=False,
            automatic_scaling=automatic_scaling,
            skip_files=r'\*.gif',
            error_handlers=error_handlers,
            handlers=handlers,
            inbound_services=['warmup'],
            env_variables=env_variables,
        )
        backend_entry = backendinfo.BackendEntry(name='static',
                                                 instances='3',
                                                 options='public')

        application_configuration.ModuleConfiguration._parse_configuration(
            '/appdir/app.yaml').AndReturn((info, []))
        os.path.getmtime('/appdir/app.yaml').AndReturn(10)

        self.mox.ReplayAll()
        module_config = application_configuration.ModuleConfiguration(
            '/appdir/app.yaml')
        config = application_configuration.BackendConfiguration(
            module_config, None, backend_entry)
        self.mox.VerifyAll()

        self.assertEqual(os.path.realpath('/appdir'), config.application_root)
        self.assertEqual('app', config.application)
        self.assertEqual('static', config.module_name)
        self.assertEqual('1', config.major_version)
        self.assertRegexpMatches(config.version_id, r'static:1\.\d+')
        self.assertEqual('python27', config.runtime)
        self.assertFalse(config.threadsafe)
        self.assertEqual(None, config.automatic_scaling)
        self.assertEqual(None, config.basic_scaling)
        self.assertEqual(appinfo.ManualScaling(instances='3'),
                         config.manual_scaling)
        self.assertEqual(info.GetNormalizedLibraries(),
                         config.normalized_libraries)
        self.assertEqual(r'\*.gif', config.skip_files)
        self.assertEqual(error_handlers, config.error_handlers)
        self.assertEqual(handlers, config.handlers)
        self.assertEqual(['warmup'], config.inbound_services)
        self.assertEqual(env_variables, config.env_variables)

        whitelist_fields = [
            'module_name', 'version_id', 'automatic_scaling', 'manual_scaling',
            'basic_scaling', 'is_backend'
        ]
        # Check that all public attributes and methods in a ModuleConfiguration
        # exist in a BackendConfiguration.
        for field in dir(module_config):
            if not field.startswith('_'):
                self.assertTrue(hasattr(config, field),
                                'Missing field: %s' % field)
                value = getattr(module_config, field)
                if field not in whitelist_fields and not callable(value):
                    # Check that the attributes other than those in the whitelist have
                    # equal values in the BackendConfiguration to the ModuleConfiguration
                    # from which it inherits.
                    self.assertEqual(value, getattr(config, field))