| #!/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 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, |
| web_xml_str, |
| static_files, |
| api_version='1.0'): |
| """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 |
| 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() |
| web_parser = web_xml_parser.WebXmlParser() |
| app_engine_web_xml = aewx_parser.ProcessXml(app_engine_web_xml_str) |
| web_xml = web_parser.ProcessXml(web_xml_str) |
| translator = AppYamlTranslator( |
| app_engine_web_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 |
| """ |
| |
| def __init__(self, |
| app_engine_web_xml, |
| web_xml, |
| static_files, |
| api_version): |
| |
| self.app_engine_web_xml = app_engine_web_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.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 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)) |