236 lines
8.2 KiB
Python
236 lines
8.2 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.
|
|
|
|
"""Package containing fingerprinting for all runtimes.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from gae_ext_runtime import ext_runtime
|
|
|
|
from googlecloudsdk.api_lib.app import ext_runtime_adapter
|
|
from googlecloudsdk.api_lib.app.runtimes import python
|
|
from googlecloudsdk.api_lib.app.runtimes import python_compat
|
|
from googlecloudsdk.core import exceptions
|
|
from googlecloudsdk.core import log
|
|
|
|
RUNTIMES = [
|
|
# Note that ordering of runtimes here is very important and changes to the
|
|
# relative positions need to be tested carefully.
|
|
|
|
# Custom comes first, if we've got a Dockerfile this is a custom runtime.
|
|
ext_runtime_adapter.CoreRuntimeLoader('custom', 'Custom',
|
|
['custom']),
|
|
|
|
# Go's position is relatively flexible due to its orthogonal nature.
|
|
ext_runtime_adapter.CoreRuntimeLoader('go', 'Go', ['go', 'custom']),
|
|
|
|
ext_runtime_adapter.CoreRuntimeLoader('ruby', 'Ruby', ['ruby', 'custom']),
|
|
ext_runtime_adapter.CoreRuntimeLoader('nodejs', 'Node.js',
|
|
['nodejs', 'custom']),
|
|
ext_runtime_adapter.CoreRuntimeLoader('java', 'Java',
|
|
['java', 'java7', 'custom']),
|
|
python_compat,
|
|
|
|
# Python and PHP are last because they match if any .py or .php file is
|
|
# present.
|
|
ext_runtime_adapter.CoreRuntimeLoader('python', 'Python',
|
|
['python', 'custom']),
|
|
ext_runtime_adapter.CoreRuntimeLoader('php', 'PHP', ['php', 'custom']),
|
|
]
|
|
|
|
|
|
class UnidentifiedDirectoryError(exceptions.Error):
|
|
"""Raised when GenerateConfigs() can't identify the directory."""
|
|
|
|
def __init__(self, path):
|
|
"""Constructor.
|
|
|
|
Args:
|
|
path: (basestring) Directory we failed to identify.
|
|
"""
|
|
super(UnidentifiedDirectoryError, self).__init__(
|
|
'Unrecognized directory type: [{0}]'.format(path))
|
|
self.path = path
|
|
|
|
|
|
class ExtRuntimeError(exceptions.Error):
|
|
"""ext_runtime.Error errors are converted to this."""
|
|
|
|
|
|
class ConflictingConfigError(exceptions.Error):
|
|
"""Property in app.yaml conflicts with params passed to fingerprinter."""
|
|
|
|
|
|
class AlterConfigFileError(exceptions.Error):
|
|
"""Error when attempting to update an existing config file (app.yaml)."""
|
|
|
|
def __init__(self, inner_exception):
|
|
super(AlterConfigFileError, self).__init__(
|
|
'Could not alter app.yaml due to an internal error:\n{0}\n'
|
|
'Please update app.yaml manually.'.format(inner_exception))
|
|
|
|
|
|
def IdentifyDirectory(path, params=None):
|
|
"""Try to identify the given directory.
|
|
|
|
As a side-effect, if there is a config file in 'params' with a runtime of
|
|
'custom', this sets params.custom to True.
|
|
|
|
Args:
|
|
path: (basestring) Root directory to identify.
|
|
params: (ext_runtime.Params or None) Parameters passed through to the
|
|
fingerprinters. Uses defaults if not provided.
|
|
|
|
Returns:
|
|
(ext_runtime.Configurator or None) Returns a module if we've identified
|
|
it, None if not.
|
|
"""
|
|
if not params:
|
|
params = ext_runtime.Params()
|
|
|
|
# Parameter runtime has precedence
|
|
if params.runtime:
|
|
specified_runtime = params.runtime
|
|
elif params.appinfo:
|
|
specified_runtime = params.appinfo.GetEffectiveRuntime()
|
|
else:
|
|
specified_runtime = None
|
|
|
|
if specified_runtime == 'custom':
|
|
params.custom = True
|
|
|
|
for runtime in RUNTIMES:
|
|
|
|
# If we have an app.yaml, don't fingerprint for any runtimes that don't
|
|
# allow the runtime name it specifies.
|
|
if (specified_runtime and runtime.ALLOWED_RUNTIME_NAMES and
|
|
specified_runtime not in runtime.ALLOWED_RUNTIME_NAMES):
|
|
log.info('Not checking for [%s] because runtime is [%s]' %
|
|
(runtime.NAME, specified_runtime))
|
|
continue
|
|
|
|
try:
|
|
configurator = runtime.Fingerprint(path, params)
|
|
except ext_runtime.Error as ex:
|
|
raise ExtRuntimeError(ex.message)
|
|
if configurator:
|
|
return configurator
|
|
return None
|
|
|
|
|
|
def _GetModule(path, params=None, config_filename=None):
|
|
"""Helper function for generating configs.
|
|
|
|
Args:
|
|
path: (basestring) Root directory to identify.
|
|
params: (ext_runtime.Params or None) Parameters passed through to the
|
|
fingerprinters. Uses defaults if not provided.
|
|
config_filename: (str or None) Filename of the config file (app.yaml).
|
|
|
|
Raises:
|
|
UnidentifiedDirectoryError: No runtime module matched the directory.
|
|
ConflictingConfigError: Current app.yaml conflicts with other params.
|
|
|
|
Returns:
|
|
ext_runtime.Configurator, the configurator for the path
|
|
"""
|
|
if not params:
|
|
params = ext_runtime.Params()
|
|
|
|
config = params.appinfo
|
|
# An app.yaml exists, results in a lot more cases
|
|
if config and not params.deploy:
|
|
# Enforce --custom
|
|
if not params.custom:
|
|
raise ConflictingConfigError(
|
|
'Configuration file already exists. This command generates an '
|
|
'app.yaml configured to run an application on Google App Engine. '
|
|
'To create the configuration files needed to run this '
|
|
'application with docker, try `gcloud preview app gen-config '
|
|
'--custom`.')
|
|
# Check that current config is for MVM
|
|
if not config.IsVm():
|
|
raise ConflictingConfigError(
|
|
'gen-config is only supported for App Engine Flexible. Please '
|
|
'use "vm: true" in your app.yaml if you would like to use App Engine '
|
|
'Flexible to run your application.')
|
|
# Check for conflicting --runtime and runtime in app.yaml
|
|
if (config.GetEffectiveRuntime() != 'custom' and params.runtime is not None
|
|
and params.runtime != config.GetEffectiveRuntime()):
|
|
raise ConflictingConfigError(
|
|
'[{0}] contains "runtime: {1}" which conficts with '
|
|
'--runtime={2}.'.format(config_filename, config.GetEffectiveRuntime(),
|
|
params.runtime))
|
|
|
|
module = IdentifyDirectory(path, params)
|
|
if not module:
|
|
raise UnidentifiedDirectoryError(path)
|
|
return module
|
|
|
|
|
|
def GenerateConfigs(path, params=None, config_filename=None):
|
|
"""Identify runtime and generate config files for a directory.
|
|
|
|
If a runtime can be identified for the given directory, calls the runtime's
|
|
GenerateConfigs method, which writes configs to the directory.
|
|
|
|
Args:
|
|
path: (basestring) Root directory to identify.
|
|
params: (ext_runtime.Params or None) Parameters passed through to the
|
|
fingerprinters. Uses defaults if not provided.
|
|
config_filename: (str or None) Filename of the config file (app.yaml).
|
|
|
|
Raises:
|
|
ExtRuntimeError: if there was an error generating configs
|
|
|
|
Returns:
|
|
(bool): True if files were written
|
|
"""
|
|
module = _GetModule(path, params=params, config_filename=config_filename)
|
|
|
|
try:
|
|
return module.GenerateConfigs()
|
|
except ext_runtime.Error as ex:
|
|
raise ExtRuntimeError(ex.message)
|
|
|
|
|
|
def GenerateConfigData(path, params=None, config_filename=None):
|
|
"""Identify runtime and generate contents of config files for a directory.
|
|
|
|
If a runtime can be identified for the given directory, calls the runtime's
|
|
GenerateConfigData method, which generates the contents of config files.
|
|
|
|
Args:
|
|
path: (basestring) Root directory to identify.
|
|
params: (ext_runtime.Params or None) Parameters passed through to the
|
|
fingerprinters. Uses defaults if not provided.
|
|
config_filename: (str or None) Filename of the config file (app.yaml).
|
|
|
|
Raises:
|
|
ExtRuntimeError: if there was an error generating configs
|
|
|
|
Returns:
|
|
[ext_runtime.GeneratedFile] generated config files.
|
|
"""
|
|
module = _GetModule(path, params=params, config_filename=config_filename)
|
|
|
|
try:
|
|
return module.GenerateConfigData()
|
|
except ext_runtime.Error as ex:
|
|
raise ExtRuntimeError(ex.message)
|