blob: bfd17864ed97436fe0af76a6da057e7f2092a056 [file] [log] [blame]
# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import logging
import re
from google.appengine.api import app_identity
from google.appengine.api import lib_config
from google.appengine.ext import ndb
from protorpc import messages
# Config component is using google.protobuf package, it requires some python
# package magic hacking.
from components import utils
utils.fix_protobuf_package()
from google import protobuf
from components import auth
from components import protoutil
from components import utils
from components.datastore_utils import config
class Severity(messages.Enum):
DEBUG = logging.DEBUG
INFO = logging.INFO
WARNING = logging.WARNING
ERROR = logging.ERROR
CRITICAL = logging.CRITICAL
################################################################################
# Patterns
SERVICE_ID_PATTERN = r'[a-z0-9\-_]+'
SERVICE_ID_RGX = re.compile(r'^%s$' % SERVICE_ID_PATTERN)
SERVICE_CONFIG_SET_RGX = re.compile(r'^services/(%s)$' % SERVICE_ID_PATTERN)
PROJECT_ID_PATTERN = r'[a-z0-9\-]{1,30}'
PROJECT_ID_RGX = re.compile(r'^%s$' % PROJECT_ID_PATTERN)
PROJECT_CONFIG_SET_RGX = re.compile(r'^projects/(%s)$' % PROJECT_ID_PATTERN)
REF_NAME_PATTERN = r'refs/.+'
REF_NAME_RGX = re.compile(r'^%s$' % REF_NAME_PATTERN)
REF_CONFIG_SET_RGX = re.compile(
r'^projects/(%s)/(%s)$' % (PROJECT_ID_PATTERN, REF_NAME_PATTERN))
ALL_CONFIG_SET_RGX = [
SERVICE_CONFIG_SET_RGX,
PROJECT_CONFIG_SET_RGX,
REF_CONFIG_SET_RGX,
]
################################################################################
# Settings
class ConstantConfig(object):
# In filesystem mode, the directory where configs are read from.
CONFIG_DIR = 'configs'
CONSTANTS = lib_config.register('components_config', ConstantConfig.__dict__)
class ConfigSettings(config.GlobalConfig):
# Hostname of the config service.
service_hostname = ndb.StringProperty(indexed=False)
# Identity account used by config service.
trusted_config_account = auth.IdentityProperty(indexed=False)
################################################################################
# Config parsing
class ConfigFormatError(Exception):
"""A config file is malformed."""
def _validate_dest_type(dest_type):
if dest_type is None:
return
if not issubclass(dest_type, protobuf.message.Message):
raise NotImplementedError('%s type is not supported' % dest_type.__name__)
def _convert_config(content, dest_type):
_validate_dest_type(dest_type)
if dest_type is None or isinstance(content, dest_type):
return content
if content is None:
return None
msg = dest_type()
try:
protobuf.text_format.Merge(
protoutil.parse_multiline(content.decode('utf-8')), msg)
except (protoutil.MultilineParseError, protobuf.text_format.ParseError,
UnicodeDecodeError) as ex:
raise ConfigFormatError(ex.message)
return msg
################################################################################
# Rest
def _trim_app_id(app_id):
"""Returns the App ID with the domain prefix removed, if present."""
return app_id.split(':')[-1]
@utils.cache
def self_config_set():
return 'services/%s' % _trim_app_id(app_identity.get_application_id())
def config_service_hostname():
"""Returns hostname of the config service, or None."""
settings = ConfigSettings.cached()
return settings.service_hostname if settings else None