177 lines
5.3 KiB
Python
177 lines
5.3 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2015 Google LLC. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""Utilities for the dev_appserver.py wrapper script.
|
|
|
|
Functions for parsing app.yaml files and installing the required components.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import argparse
|
|
import os
|
|
|
|
from googlecloudsdk.core import yaml
|
|
import six
|
|
|
|
# Runtime ID to component mapping. python27-libs is a special token indicating
|
|
# that the real runtime id is python27, and that a libraries section has been
|
|
# specified in the app.yaml.
|
|
_RUNTIME_COMPONENTS = {
|
|
'java': 'app-engine-java',
|
|
'go': 'app-engine-go',
|
|
'python27-libs': 'app-engine-python-extras',
|
|
}
|
|
|
|
|
|
_YAML_FILE_EXTENSIONS = ('.yaml', '.yml')
|
|
|
|
|
|
_TRUE_VALUES = ['true', 'yes', '1']
|
|
|
|
|
|
_FALSE_VALUES = ['false', 'no', '0']
|
|
|
|
|
|
_UPSTREAM_DEV_APPSERVER_FLAGS = ['--support_datastore_emulator']
|
|
|
|
|
|
class MultipleAppYamlError(Exception):
|
|
"""An application configuration has more than one valid app yaml files."""
|
|
|
|
|
|
def GetRuntimes(args):
|
|
"""Gets a list of unique runtimes that the user is about to run.
|
|
|
|
Args:
|
|
args: A list of arguments (typically sys.argv).
|
|
|
|
Returns:
|
|
A set of runtime strings. If python27 and libraries section is populated
|
|
in any of the yaml-files, 'python27-libs', a fake runtime id, will be part
|
|
of the set, in conjunction with the original 'python27'.
|
|
|
|
Raises:
|
|
MultipleAppYamlError: The supplied application configuration has duplicate
|
|
app yamls.
|
|
"""
|
|
runtimes = set()
|
|
for arg in args:
|
|
# Check all the arguments to see if they're application yaml files or
|
|
# directories that include yaml files.
|
|
yaml_candidate = None
|
|
if (os.path.isfile(arg) and
|
|
os.path.splitext(arg)[1] in _YAML_FILE_EXTENSIONS):
|
|
yaml_candidate = arg
|
|
elif os.path.isdir(arg):
|
|
for extension in _YAML_FILE_EXTENSIONS:
|
|
fullname = os.path.join(arg, 'app' + extension)
|
|
if os.path.isfile(fullname):
|
|
if yaml_candidate:
|
|
raise MultipleAppYamlError(
|
|
'Directory "{0}" contains conflicting files {1}'.format(
|
|
arg, ' and '.join(yaml_candidate)))
|
|
|
|
yaml_candidate = fullname
|
|
|
|
if yaml_candidate:
|
|
try:
|
|
info = yaml.load_path(yaml_candidate)
|
|
except yaml.Error:
|
|
continue
|
|
|
|
# safe_load can return arbitrary objects, we need a dict.
|
|
if not isinstance(info, dict):
|
|
continue
|
|
# Grab the runtime from the yaml, if it exists.
|
|
if 'runtime' in info:
|
|
runtime = info.get('runtime')
|
|
if type(runtime) == str:
|
|
if runtime == 'python27' and info.get('libraries'):
|
|
runtimes.add('python27-libs')
|
|
runtimes.add(runtime)
|
|
elif os.path.isfile(os.path.join(arg, 'WEB-INF', 'appengine-web.xml')):
|
|
# For unstanged Java App Engine apps, which may not have any yaml files.
|
|
runtimes.add('java')
|
|
return runtimes
|
|
|
|
|
|
def GetComponents(runtimes):
|
|
"""Gets a list of required components.
|
|
|
|
Args:
|
|
runtimes: A list containing the required runtime ids.
|
|
Returns:
|
|
A list of components that must be present.
|
|
"""
|
|
# Always install python.
|
|
components = ['app-engine-python']
|
|
for requested_runtime in runtimes:
|
|
for component_runtime, component in six.iteritems(_RUNTIME_COMPONENTS):
|
|
if component_runtime in requested_runtime:
|
|
components.append(component)
|
|
return components
|
|
|
|
|
|
def _ParseBoolean(value):
|
|
"""This is upstream logic from dev_appserver for parsing boolean arguments.
|
|
|
|
Args:
|
|
value: value assigned to a flag.
|
|
|
|
Returns:
|
|
A boolean parsed from value.
|
|
|
|
Raises:
|
|
ValueError: value.lower() is not in _TRUE_VALUES + _FALSE_VALUES.
|
|
"""
|
|
if isinstance(value, bool):
|
|
return value
|
|
if value:
|
|
value = value.lower()
|
|
if value in _TRUE_VALUES:
|
|
return True
|
|
if value in _FALSE_VALUES:
|
|
return False
|
|
repr_value = (repr(value) for value in _TRUE_VALUES + _FALSE_VALUES)
|
|
raise ValueError('%r unrecognized boolean; known booleans are %s.' %
|
|
(value, ', '.join(repr_value)))
|
|
return True
|
|
|
|
|
|
def ParseDevAppserverFlags(args):
|
|
"""Parse flags from app engine dev_appserver.py.
|
|
|
|
Only the subset of args are parsed here. These args are listed in
|
|
_UPSTREAM_DEV_APPSERVER_FLAGS.
|
|
|
|
Args:
|
|
args: A list of arguments (typically sys.argv).
|
|
|
|
Returns:
|
|
options: An argparse.Namespace containing the command line arguments.
|
|
"""
|
|
upstream_args = [
|
|
arg for arg in args if
|
|
any(arg.startswith(upstream_arg) for upstream_arg
|
|
in _UPSTREAM_DEV_APPSERVER_FLAGS)]
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
'--support_datastore_emulator', dest='support_datastore_emulator',
|
|
type=_ParseBoolean, const=True, nargs='?', default=False)
|
|
return parser.parse_args(upstream_args)
|