blob: e85ea6ed0e7a988c52d316841ce18268322e38a7 [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.
#
"""Performs XML-to-YAML translation.
TranslateXmlToYaml(): performs xml-to-yaml translation with
string inputs and outputs
AppYamlTranslator: Class that facilitates xml-to-yaml translation
"""
from google.appengine.tools import app_engine_web_xml_parser as aewxp
from google.appengine.tools import backends_xml_parser
from google.appengine.tools import handler_generator
from google.appengine.tools import web_xml_parser
from google.appengine.tools.app_engine_web_xml_parser import AppEngineConfigException
NO_API_VERSION = 'none'
def TranslateXmlToYaml(app_engine_web_xml_str,
backends_xml_str,
web_xml_str,
static_files,
api_version):
"""Does xml-string to yaml-string translation, given each separate file text.
Processes each xml string into an object representing the xml,
and passes these to the translator.
Args:
app_engine_web_xml_str: text from app_engine_web.xml
backends_xml_str: text from backends.xml
web_xml_str: text from web.xml
static_files: List of static files
api_version: current api version
Returns:
The full text of the app.yaml generated from the xml files.
Raises:
AppEngineConfigException: raised in processing stage for illegal XML.
"""
aewx_parser = aewxp.AppEngineWebXmlParser()
backends_parser = backends_xml_parser.BackendsXmlParser()
web_parser = web_xml_parser.WebXmlParser()
app_engine_web_xml = aewx_parser.ProcessXml(app_engine_web_xml_str)
backends_xml = backends_parser.ProcessXml(backends_xml_str)
web_xml = web_parser.ProcessXml(web_xml_str)
translator = AppYamlTranslator(
app_engine_web_xml, backends_xml, web_xml, static_files, api_version)
return translator.GetYaml()
def GetRuntime():
return 'java7'
class AppYamlTranslator(object):
"""Object that contains relevant information for generating app.yaml.
Attributes:
app_engine_web_xml: AppEngineWebXml object containing relevant information
from appengine-web.xml
backends_xml: BackendsXml object containing relevant info from backends.xml
"""
def __init__(self,
app_engine_web_xml,
backends_xml,
web_xml,
static_files,
api_version):
self.app_engine_web_xml = app_engine_web_xml
self.backends_xml = backends_xml
self.web_xml = web_xml
self.static_files = static_files
self.api_version = api_version
def GetYaml(self):
"""Returns full yaml text."""
self.VerifyRequiredEntriesPresent()
stmnt_list = self.TranslateBasicEntries()
stmnt_list += self.TranslateAutomaticScaling()
stmnt_list += self.TranslateBasicScaling()
stmnt_list += self.TranslateManualScaling()
stmnt_list += self.TranslatePrecompilationEnabled()
stmnt_list += self.TranslateInboundServices()
stmnt_list += self.TranslateAdminConsolePages()
stmnt_list += self.TranslateApiConfig()
stmnt_list += self.TranslatePagespeed()
stmnt_list += self.TranslateVmSettings()
stmnt_list += self.TranslateErrorHandlers()
stmnt_list += self.TranslateBackendsXml()
stmnt_list += self.TranslateApiVersion()
stmnt_list += self.TranslateHandlers()
return '\n'.join(stmnt_list) + '\n'
def SanitizeForYaml(self, the_string):
return "'%s'" % the_string.replace("'", "''")
def TranslateBasicEntries(self):
"""Produces yaml for entries requiring little formatting."""
basic_statements = []
for entry_name, field in [
('application', self.app_engine_web_xml.app_id),
('source_language', self.app_engine_web_xml.source_language),
('module', self.app_engine_web_xml.module),
('version', self.app_engine_web_xml.version_id)]:
if field:
basic_statements.append(
'%s: %s' % (entry_name, self.SanitizeForYaml(field)))
for entry_name, field in [
('runtime', GetRuntime()),
('vm', self.app_engine_web_xml.vm),
('threadsafe', self.app_engine_web_xml.threadsafe),
('instance_class', self.app_engine_web_xml.instance_class),
('auto_id_policy', self.app_engine_web_xml.auto_id_policy),
('code_lock', self.app_engine_web_xml.codelock)]:
if field:
basic_statements.append('%s: %s' % (entry_name, field))
return basic_statements
def TranslateAutomaticScaling(self):
"""Translates automatic scaling settings to yaml."""
if not self.app_engine_web_xml.automatic_scaling:
return []
statements = ['automatic_scaling:']
for setting in ['min_pending_latency',
'max_pending_latency',
'min_idle_instances',
'max_idle_instances']:
value = getattr(self.app_engine_web_xml.automatic_scaling, setting)
if value:
statements.append(' %s: %s' % (setting, value))
return statements
def TranslateBasicScaling(self):
if not self.app_engine_web_xml.basic_scaling:
return []
statements = ['basic_scaling:']
statements.append(' max_instances: ' +
self.app_engine_web_xml.basic_scaling.max_instances)
if self.app_engine_web_xml.basic_scaling.idle_timeout:
statements.append(' idle_timeout: ' +
self.app_engine_web_xml.basic_scaling.idle_timeout)
return statements
def TranslateManualScaling(self):
if not self.app_engine_web_xml.manual_scaling:
return []
statements = ['manual_scaling:']
statements.append(' instances: ' +
self.app_engine_web_xml.manual_scaling.instances)
return statements
def TranslatePrecompilationEnabled(self):
if self.app_engine_web_xml.precompilation_enabled:
return ['derived_file_type:', '- java_precompiled']
return []
def TranslateAdminConsolePages(self):
if not self.app_engine_web_xml.admin_console_pages:
return []
statements = ['admin_console:', ' pages:']
for admin_console_page in self.app_engine_web_xml.admin_console_pages:
statements.append(' - name: %s' % admin_console_page.name)
statements.append(' url: %s' % admin_console_page.url)
return statements
def TranslateApiConfig(self):
if not self.app_engine_web_xml.api_config:
return []
return ['api_config:', ' url: %s' % self.app_engine_web_xml.api_config.url,
' script: unused']
def TranslateApiVersion(self):
return ['api_version: %s' % self.SanitizeForYaml(
self.api_version or NO_API_VERSION)]
def TranslatePagespeed(self):
"""Translates pagespeed settings in appengine-web.xml to yaml."""
pagespeed = self.app_engine_web_xml.pagespeed
if not pagespeed:
return []
statements = ['pagespeed:']
for title, urls in [('domains_to_rewrite', pagespeed.domains_to_rewrite),
('url_blacklist', pagespeed.url_blacklist),
('enabled_rewriters', pagespeed.enabled_rewriters),
('disabled_rewriters', pagespeed.disabled_rewriters)]:
if urls:
statements.append(' %s:' % title)
statements += [' - %s' % url for url in urls]
return statements
def TranslateVmSettings(self):
"""Translates VM settings in appengine-web.xml to yaml."""
if (not self.app_engine_web_xml.vm or
not self.app_engine_web_xml.vm_settings):
return []
settings = self.app_engine_web_xml.vm_settings
statements = ['vm_settings:']
for name in sorted(settings):
statements.append(
' %s: %s' % (
self.SanitizeForYaml(name), self.SanitizeForYaml(settings[name])))
return statements
def TranslateInboundServices(self):
services = self.app_engine_web_xml.inbound_services
if not services:
return []
statements = ['inbound_services:']
for service in sorted(services):
statements.append('- %s' % service)
return statements
def TranslateErrorHandlers(self):
"""Translates error handlers specified in appengine-web.xml to yaml."""
if not self.app_engine_web_xml.static_error_handlers:
return []
statements = ['error_handlers:']
for error_handler in self.app_engine_web_xml.static_error_handlers:
name = error_handler.name
if not name.startswith('/'):
name = '/' + name
if ('__static__' + name) not in self.static_files:
raise AppEngineConfigException(
'No static file found for error handler: %s, out of %s' %
(name, self.static_files))
statements.append('- file: __static__%s' % name)
if error_handler.code:
statements.append(' error_code: %s' % error_handler.code)
mime_type = self.web_xml.GetMimeTypeForPath(name)
if mime_type:
statements.append(' mime_type: %s' % mime_type)
return statements
def TranslateBackendsXml(self):
"""Translates backends.xml backends settings to yaml."""
if not self.backends_xml:
return []
statements = ['backends:']
for backend in self.backends_xml:
statements.append('- name: %s' % backend.name)
for entry, field in [('instances', backend.instances),
('instance_class', backend.instance_class),
('max_concurrent_requests',
backend.max_concurrent_requests)]:
if field is not None:
statements.append(' %s: %s' % (entry, str(field)))
if backend.options:
options_str = ', '.join(sorted(list(backend.options)))
statements.append(' options: %s' % options_str)
return statements
def TranslateHandlers(self):
return handler_generator.GenerateYamlHandlersList(
self.app_engine_web_xml,
self.web_xml,
self.static_files)
def VerifyRequiredEntriesPresent(self):
required = {
'app_id': self.app_engine_web_xml.app_id,
'version_id': self.app_engine_web_xml.version_id,
'runtime': GetRuntime(),
'threadsafe': self.app_engine_web_xml.threadsafe_value_provided,
}
missing = [field for (field, value) in required.items() if not value]
if missing:
raise AppEngineConfigException('Missing required fields: %s' %
', '.join(missing))