Source code for gaeconf.gaeconf

# -*- coding: utf-8 -*-
import inspect
import ruamel.yaml as yaml
from os import environ


[docs]class AppEngineBaseConfig: """ This base class is implements a `to_yaml()` method which dumps a YAML file compatible with GAE. Instructions ------------ - Constructor receives an optional `exposed_env_vars` parameter to define the host environment variables which will be passed to the resulting GAE app.yaml file. - The attributes of the class will be exported to the final yaml. - An attribute starting with __ENV__ will be considered an environment variable and it will be included to env_variables in the yaml. - Some of the class attributes can be ignored by overriding `get_ignore_attrs` method. """ service = None runtime = None def __init__(self, exposed_env_vars=None): self._exposed_env_vars = exposed_env_vars or []
[docs] def clean_config(self, config: dict) -> dict: """ Config post-processing :param config: App engine configuration vars :type config: dict :return: dict """ return config
[docs] def get_ignore_attrs(self, data: list) -> list: """ Exclude class attrbutes from the yaml export :param data: List of class attributes :type data: list :return: list """ data.append('ignore_attrs') return data
[docs] def to_yaml(self) -> str: """ Dumps the current configuration as yaml :return: str """ methods = [e[0] for e in inspect.getmembers(self, predicate=inspect.ismethod)] # Class attrs ending with __ENC are attributes whose values were previously encrypted # and then decrypted, but it is necessary to get the real attribute name without the # __ENC config = { e.rsplit('__ENC', 1)[0]: getattr(self, e) for e in dir(self) if not e.startswith('_') and e not in methods and e not in self.get_ignore_attrs([]) } # Filter class attrs starting with __ENV__ and adds them to env_vars env_vars = {e.split('__ENV__')[1].rsplit('__ENC', 1)[0]: getattr(self, e) for e in dir(self) if '__ENV__' in e} # Include exposed environment variables in env_variables for e in self._exposed_env_vars: env_var_value = environ.get(e, '') if env_var_value: env_vars[e] = env_var_value config["env_variables"] = env_vars return yaml.safe_dump(self.clean_config(config), default_flow_style=False)
[docs]class AppEngineStandardConfig(AppEngineBaseConfig): """ Defines a suitable configuration for Google App Engine standard runtime. By default the runtime is *python37* and the instance class is *F4_HIGHMEM* but these defaults can be overriden with the following environment variables: - `GAECONF_STANDARD_RUNTIME` - `GAECONF_STANDARD_INSTANCE_CLASS` """ runtime = environ.get("GAECONF_STANDARD_RUNTIME", "python37") instance_class = environ.get("GAECONF_STANDARD_INSTANCE_CLASS", "F4_HIGHMEM")
[docs]class AppEngineFlexibleConfig(AppEngineBaseConfig): """ Defines a suitable configuration for Google App Engine flexible runtime. Default configuration can be overriden with environment variables: - `GAECONF_FLEXIBLE_RUNTIME`: *python* - `GAECONF_FLEXIBLE_RUNTIME_PYTHON_VERSION`: *3.7* - `GAECONF_AUTOSCALING_MAX_INSTANCES`: *2* - `GAECONF_AUTOSCALING_MIN_INSTANCES`: *1* - `GAECONF_FLEXIBLE_NUM_CPU`: *2* - `GAECONF_FLEXIBLE_MEMORY_GB`: *3* - `GAECONF_FLEXIBLE_DISK_SIZE_GB`: *20* """ service = None instance_tag = None subnetwork_name = None runtime = environ.get("GAECONF_FLEXIBLE_RUNTIME", "python") automatic_scaling = { "max_num_instances": int(environ.get("GAECONF_AUTOSCALING_MAX_INSTANCES", 2)), "min_num_instances": int(environ.get("GAECONF_AUTOSCALING_MIN_INSTANCES", 1)), } resources = { "cpu": int(environ.get("GAECONF_FLEXIBLE_NUM_CPU", 2)), "memory_gb": int(environ.get("GAECONF_FLEXIBLE_MEMORY_GB", 3)), "disk_size_gb": int(environ.get("GAECONF_FLEXIBLE_DISK_SIZE_GB", 20)) } runtime_config = { "python_version": environ.get("GAECONF_FLEXIBLE_RUNTIME_PYTHON_VERSION", "3.7") } env = 'flex'
[docs] def get_ignore_attrs(self, data: list) -> list: data = super().get_ignore_attrs(data) data.extend(['instance_tag', 'subnetwork_name']) return data
[docs] def clean_config(self, config: dict) -> dict: config['network'] = { "instance_tag": self.instance_tag or self.service, "name": "default", "subnetwork_name": self.subnetwork_name } return super().clean_config(config)
[docs]def load_yaml_classes(filename: str) -> dict: """ Parses a YAML file and builds Python classes defining the GAE services. :param filename: path to a yaml configuration file :type filename: str :returns: {<service-name>: <service-class>} """ with open(filename) as stream: obj = yaml.load(stream, Loader=yaml.Loader) services = {} for class_name, config in obj['services'].items(): parent_classes = {k.__name__: k for k in services.values()} parents = tuple([parent_classes[e] for e in tuple(config.pop('inherits_from', []))]) attrs = config.copy() env_vars = attrs.pop('env_variables', {}) for k, v in env_vars.items(): attrs[f'__ENV__{k}'] = v if 'service' in config: services[config["service"]] = type(class_name, parents, attrs) else: services[class_name] = type(class_name, parents, attrs) return services