def __init__(self, config_paths, app_id=None):
        """Initializer for ApplicationConfiguration.

    Args:
      config_paths: A list of strings containing the paths to yaml files,
          or to directories containing them.
      app_id: A string that is the application id, or None if the application id
          from the yaml or xml file should be used.
    Raises:
      InvalidAppConfigError: On invalid configuration.
    """
        self.modules = []
        self.dispatch = None
        # It's really easy to add a test case that passes in a string rather than
        # a list of strings, so guard against that.
        assert not isinstance(config_paths, basestring)
        config_paths = self._config_files_from_paths(config_paths)
        for config_path in config_paths:
            # TODO: add support for backends.xml and dispatch.xml here
            if (config_path.endswith('backends.yaml')
                    or config_path.endswith('backends.yml')):
                # TODO: Reuse the ModuleConfiguration created for the app.yaml
                # instead of creating another one for the same file.
                app_yaml = config_path.replace('backends.y', 'app.y')
                self.modules.extend(
                    BackendsConfiguration(app_yaml, config_path,
                                          app_id).get_backend_configurations())
            elif (config_path.endswith('dispatch.yaml')
                  or config_path.endswith('dispatch.yml')):
                if self.dispatch:
                    raise errors.InvalidAppConfigError(
                        'Multiple dispatch.yaml files specified')
                self.dispatch = DispatchConfiguration(config_path)
            else:
                module_configuration = ModuleConfiguration(config_path, app_id)
                self.modules.append(module_configuration)
        application_ids = set(module.application for module in self.modules)
        if len(application_ids) > 1:
            raise errors.InvalidAppConfigError(
                'More than one application ID found: %s' %
                ', '.join(sorted(application_ids)))

        self._app_id = application_ids.pop()
        module_names = set()
        for module in self.modules:
            if module.module_name in module_names:
                raise errors.InvalidAppConfigError('Duplicate module: %s' %
                                                   module.module_name)
            module_names.add(module.module_name)
        if self.dispatch:
            if appinfo.DEFAULT_MODULE not in module_names:
                raise errors.InvalidAppConfigError(
                    'A default module must be specified.')
            missing_modules = (
                set(module_name for _, module_name in self.dispatch.dispatch) -
                module_names)
            if missing_modules:
                raise errors.InvalidAppConfigError(
                    'Modules %s specified in dispatch.yaml are not defined by a yaml '
                    'file.' % sorted(missing_modules))
Ejemplo n.º 2
0
 def _files_in_dir_matching(dir_path, names):
     abs_names = [os.path.join(dir_path, name) for name in names]
     files = [f for f in abs_names if os.path.exists(f)]
     if len(files) > 1:
         raise errors.InvalidAppConfigError('Directory "%s" contains %s' %
                                            (dir_path, ' and '.join(names)))
     return files
    def __init__(self, root_path, url_map, app_info_default_expiration=None):
        """Initializer for StaticDirHandler.

    Args:
      root_path: A string containing the full path of the directory containing
          the application's app.yaml file.
      url_map: An appinfo.URLMap instance containing the configuration for this
          handler.
      app_info_default_expiration: A string containing the value of the
          default_expiration value from app info yaml. None if no
          default_expiration is set.
    """
        url = url_map.url
        # Take a url pattern like "/css" and transform it into a match pattern like
        # "/css/(?P<file>.*)$"
        if url[-1] != '/':
            url += '/'

        try:
            url_pattern = re.compile('%s(?P<file>.*)$' % url)
        except re.error as e:
            raise errors.InvalidAppConfigError(
                'invalid url %r in static_dir handler: %s' % (url, e))

        super(StaticDirHandler, self).__init__(root_path, url_map, url_pattern,
                                               app_info_default_expiration)
Ejemplo n.º 4
0
    def __init__(self, root_path, url_map):
        """Initializer for StaticFilesHandler.

    Args:
      root_path: A string containing the full path of the directory containing
          the application's app.yaml file.
      url_map: An appinfo.URLMap instance containing the configuration for this
          handler.
    """
        try:
            url_pattern = re.compile('%s$' % url_map.url)
        except re.error, e:
            raise errors.InvalidAppConfigError(
                'invalid url %r in static_files handler: %s' %
                (url_map.url, e))
Ejemplo n.º 5
0
    def __init__(self, root_path, url_map):
        """Initializer for StaticDirHandler.

    Args:
      root_path: A string containing the full path of the directory containing
          the application's app.yaml file.
      url_map: An appinfo.URLMap instance containing the configuration for this
          handler.
    """
        url = url_map.url
        # Take a url pattern like "/css" and transform it into a match pattern like
        # "/css/(?P<file>.*)$"
        if url[-1] != '/':
            url += '/'

        try:
            url_pattern = re.compile('%s(?P<file>.*)$' % url)
        except re.error, e:
            raise errors.InvalidAppConfigError(
                'invalid url %r in static_dir handler: %s' % (url, e))
    def _files_in_dir_matching(dir_path, names):
        """Return a single-element list containing an absolute path to a file.

    The method accepts a list of filenames. If multiple are found, an error is
    raised. If only one match is found, the full path to this file is returned.

    Args:
      dir_path: A string base directory for searching for filenames.
      names: A list of string relative file names to seek within dir_path.

    Raises:
      InvalidAppConfigError: If the xml files are not found.

    Returns:
      A single-element list containing a full path to a file.
    """
        abs_names = [os.path.join(dir_path, name) for name in names]
        files = [f for f in abs_names if os.path.exists(f)]
        if len(files) > 1:
            raise errors.InvalidAppConfigError('Directory "%s" contains %s' %
                                               (dir_path, ' and '.join(names)))
        return files
    def __init__(self, root_path, url_map, app_info_default_expiration=None):
        """Initializer for StaticFilesHandler.

    Args:
      root_path: A string containing the full path of the directory containing
          the application's app.yaml file.
      url_map: An appinfo.URLMap instance containing the configuration for this
          handler.
      app_info_default_expiration: A string containing the value of the
          default_expiration value from app info yaml. None if no
          default_expiration is set.
    """
        try:
            url_pattern = re.compile('%s$' % url_map.url)
        except re.error as e:
            raise errors.InvalidAppConfigError(
                'invalid url %r in static_files handler: %s' %
                (url_map.url, e))

        super(StaticFilesHandler,
              self).__init__(root_path, url_map, url_pattern,
                             app_info_default_expiration)
    def __init__(self,
                 config_path,
                 app_id=None,
                 runtime=None,
                 env_variables=None):
        """Initializer for ModuleConfiguration.

    Args:
      config_path: A string containing the full path of the yaml or xml file
          containing the configuration for this module.
      app_id: A string that is the application id, or None if the application id
          from the yaml or xml file should be used.
      runtime: A string that is the runtime to use, or None if the runtime
          from the yaml or xml file should be used.
      env_variables: A dictionary that is the environment variables passed by
          flags.

    Raises:
      errors.DockerfileError: Raised if a user supplied a Dockerfile and a
        non-custom runtime.
      errors.InvalidAppConfigError: Raised if a user select python
        vanilla runtime.
    """
        self._config_path = config_path
        self._forced_app_id = app_id
        root = os.path.dirname(config_path)
        self._is_java = os.path.normpath(config_path).endswith(
            os.sep + 'WEB-INF' + os.sep + 'appengine-web.xml')
        if self._is_java:
            # We assume Java's XML-based config files only if config_path is
            # something like /foo/bar/WEB-INF/appengine-web.xml. In this case,
            # the application root is /foo/bar. Other apps, configured with YAML,
            # have something like /foo/bar/app.yaml, with application root /foo/bar.
            root = os.path.dirname(root)
        self._application_root = os.path.realpath(root)
        self._last_failure_message = None

        self._app_info_external, files_to_check = self._parse_configuration(
            self._config_path)

        # This if-statement is necessary because of following corner case
        # appinfo.EnvironmentVariables.Merge({}, None) returns None
        if env_variables:
            merged_env_variables = appinfo.EnvironmentVariables.Merge(
                self._app_info_external.env_variables, env_variables)
            self._app_info_external.env_variables = merged_env_variables

        self._mtimes = self._get_mtimes(files_to_check)
        self._application = '%s~%s' % (self.partition,
                                       self.application_external_name)
        self._api_version = self._app_info_external.api_version
        self._module_name = self._app_info_external.module
        self._version = self._app_info_external.version
        self._threadsafe = self._app_info_external.threadsafe
        self._basic_scaling_config = self._app_info_external.basic_scaling
        self._manual_scaling_config = self._app_info_external.manual_scaling
        self._automatic_scaling_config = self._app_info_external.automatic_scaling
        self._runtime = runtime or self._app_info_external.runtime
        self._effective_runtime = self._app_info_external.GetEffectiveRuntime()

        if self._runtime == 'python-compat':
            logging.warn(
                'The python-compat runtime is deprecated, please consider upgrading '
                'your application to use the Flexible runtime. See '
                'https://cloud.google.com/appengine/docs/flexible/python/upgrading '
                'for more details.')
        elif self._runtime == 'vm':
            logging.warn(
                'The Managed VMs runtime is deprecated, please consider migrating '
                'your application to use the Flexible runtime. See '
                'https://cloud.google.com/appengine/docs/flexible/python/migrating '
                'for more details.')

        dockerfile_dir = os.path.dirname(self._config_path)
        dockerfile = os.path.join(dockerfile_dir, 'Dockerfile')

        if self._effective_runtime != 'custom' and os.path.exists(dockerfile):
            raise errors.DockerfileError(
                'When there is a Dockerfile in the current directory, the only '
                'supported runtime is runtime: custom.  Please switch to runtime: '
                'custom.  The devappserver does not actually use your Dockerfile, so '
                'please use either the --runtime flag to specify the runtime you '
                'want or use the --custom_entrypoint flag to describe how to start '
                'your application.')

        if self._runtime == 'python':
            logging.warning(
                'The "python" runtime specified in "%s" is not supported - the '
                '"python27" runtime will be used instead. A description of the '
                'differences between the two can be found here:\n'
                'https://developers.google.com/appengine/docs/python/python25/diff27',
                self._config_path)
        self._minor_version_id = ''.join(
            random.choice(string.digits) for _ in range(18))

        self._forwarded_ports = {}
        if self.runtime == 'vm':
            # Avoid using python-vanilla with dev_appserver
            if 'python' == self._effective_runtime:
                raise errors.InvalidAppConfigError(
                    'Under dev_appserver, '
                    'runtime:python is not supported '
                    'for Flexible environment.')

            # Java uses an api_version of 1.0 where everyone else uses just 1.
            # That doesn't matter much elsewhere, but it does pain us with VMs
            # because they recognize api_version 1 not 1.0.
            # TODO: sort out this situation better, probably by changing
            # Java to use 1 like everyone else.
            if self._api_version == '1.0':
                self._api_version = '1'
            vm_settings = self._app_info_external.vm_settings
            ports = None
            if vm_settings:
                ports = vm_settings.get('forwarded_ports')
            if not ports:
                if (self._app_info_external.network
                        and self._app_info_external.network.forwarded_ports):
                    # Depending on the YAML formatting, these may be strings or ints.
                    # Force them to be strings.
                    ports = ','.join(
                        str(p) for p in
                        self._app_info_external.network.forwarded_ports)
            if ports:
                logging.debug('setting forwarded ports %s', ports)
                pm = port_manager.PortManager()
                pm.Add(ports, 'forwarded')
                self._forwarded_ports = pm.GetAllMappedPorts()['tcp']

        self._translate_configuration_files()

        # vm_health_check is deprecated but it still needs to be taken into account
        # if it is populated.
        if self._app_info_external.health_check is not None:
            health_check = self._app_info_external.health_check
        else:
            health_check = self._app_info_external.vm_health_check

        self._health_check = _set_health_check_defaults(health_check)

        # Configure the _is_{typeof}_scaling, _instance_class, and _memory_limit
        # attributes.
        self._is_manual_scaling = None
        self._is_basic_scaling = None
        self._is_automatic_scaling = None
        self._instance_class = self._app_info_external.instance_class
        if self._manual_scaling_config or self._runtime == 'vm':
            # TODO: Remove this 'or' when we support auto-scaled VMs.
            self._is_manual_scaling = True
            self._instance_class = (
                self._instance_class
                or constants.DEFAULT_MANUAL_SCALING_INSTANCE_CLASS)
        elif self._basic_scaling_config:
            self._is_basic_scaling = True
            self._instance_class = (
                self._instance_class
                or constants.DEFAULT_BASIC_SCALING_INSTANCE_CLASS)
        else:
            self._is_automatic_scaling = True
            self._instance_class = (
                self._instance_class
                or constants.DEFAULT_AUTO_SCALING_INSTANCE_CLASS)
        self._memory_limit = constants.INSTANCE_CLASS_MEMORY_LIMIT.get(
            self._instance_class)
  def __init__(self, yaml_paths):
    """Initializer for ApplicationConfiguration.

    Args:
      yaml_paths: A list of strings containing the paths to yaml files.
    """
    self.modules = []
    self.dispatch = None
    if len(yaml_paths) == 1 and os.path.isdir(yaml_paths[0]):
      directory_path = yaml_paths[0]
      for app_yaml_path in [os.path.join(directory_path, 'app.yaml'),
                            os.path.join(directory_path, 'app.yml')]:
        if os.path.exists(app_yaml_path):
          yaml_paths = [app_yaml_path]
          break
      else:
        raise errors.AppConfigNotFoundError(
            'no app.yaml file at %r' % directory_path)
      for backends_yaml_path in [os.path.join(directory_path, 'backends.yaml'),
                                 os.path.join(directory_path, 'backends.yml')]:
        if os.path.exists(backends_yaml_path):
          yaml_paths.append(backends_yaml_path)
          break
    for yaml_path in yaml_paths:
      if os.path.isdir(yaml_path):
        raise errors.InvalidAppConfigError(
            '"%s" is a directory and a yaml configuration file is required' %
            yaml_path)
      elif (yaml_path.endswith('backends.yaml') or
            yaml_path.endswith('backends.yml')):
        # TODO: Reuse the ModuleConfiguration created for the app.yaml
        # instead of creating another one for the same file.
        self.modules.extend(
            BackendsConfiguration(yaml_path.replace('backends.y', 'app.y'),
                                  yaml_path).get_backend_configurations())
      elif (yaml_path.endswith('dispatch.yaml') or
            yaml_path.endswith('dispatch.yml')):
        if self.dispatch:
          raise errors.InvalidAppConfigError(
              'Multiple dispatch.yaml files specified')
        self.dispatch = DispatchConfiguration(yaml_path)
      else:
        module_configuration = ModuleConfiguration(yaml_path)
        self.modules.append(module_configuration)
    application_ids = set(module.application
                          for module in self.modules)
    if len(application_ids) > 1:
      raise errors.InvalidAppConfigError(
          'More than one application ID found: %s' %
          ', '.join(sorted(application_ids)))

    self._app_id = application_ids.pop()
    module_names = set()
    for module in self.modules:
      if module.module_name in module_names:
        raise errors.InvalidAppConfigError('Duplicate module: %s' %
                                           module.module_name)
      module_names.add(module.module_name)
    if self.dispatch:
      if appinfo.DEFAULT_MODULE not in module_names:
        raise errors.InvalidAppConfigError(
            'A default module must be specified.')
      missing_modules = (
          set(module_name for _, module_name in self.dispatch.dispatch) -
          module_names)
      if missing_modules:
        raise errors.InvalidAppConfigError(
            'Modules %s specified in dispatch.yaml are not defined by a yaml '
            'file.' % sorted(missing_modules))