File: //snap/google-cloud-cli/396/lib/googlecloudsdk/api_lib/app/wrapper_util.py
# -*- 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)