diff --git a/.vscode/.gitignore b/.vscode/.gitignore index 08cf7faf447e96e9a9e8ff243ee4e84ddb596209..3f92a7033a197c6e6a1b11278b1a8cd1aa18e6ca 100644 --- a/.vscode/.gitignore +++ b/.vscode/.gitignore @@ -1,2 +1,3 @@ .ropeproject/ -*.sql \ No newline at end of file +*.sql +launch.json \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index a8858ee1adebe5cbbb00e99e68f7c2ba0fdddfc3..0000000000000000000000000000000000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - { - "type": "chrome", - "request": "launch", - "name": "Launch Chrome against localhost", - "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}/gui" - }, - { - "name": "Python: API Flask (0.11.x or later)", - "type": "python", - "request": "launch", - "module": "flask", - "env": { - "FLASK_APP": "nomad/api.py" - }, - "args": [ - "run", - "--port", "8000", - "--no-debugger", - "--no-reload" - ] - }, - { - "name": "Python: worker nomad.processing", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/celery", - "args": [ - "worker", "-l" , "debug", "-A", "nomad.processing" - ] - }, - { - "name": "Python: some test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_api.py::TestRepo::test_search[2-user-other_test_user]" - ] - }, - { - "name": "Python: crystal normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/crystal-tests/data/parsers/crystal/si.out]" - ] - }, - { - "name": "Python: cp2k normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/cp2k-tests/data/parsers/cp2k/si_bulk8.out]" - ] - }, - { - "name": "Python: cpmd normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/cpmd-tests/data/parsers/cpmd/geo_output.out]" - ] - }, - { - "name": "Python: wien2k normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/wien2k-tests/data/parsers/wien2k/AlN/AlN_ZB.scf]" - ] - }, - { - "name": "Python: gaussian normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/gaussian-tests/data/parsers/gaussian/Al.out]" - ] - }, - { - "name": "Python: aniline gaussian normalizer test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/gaussian-tests/data/parsers/gaussian/al-1.out]" - ] - }, - { - "name": "Python: test_parsing match test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_parsing.py::test_match" - ] - }, - { - "name": "Python: nwchem normalizer test h2o sp test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/nwchem-tests/data/parsers/nwchem/sp_output.out]" - ] - }, - { - "name": "Python: Vasp XML test", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/vasp-tests/data/parsers/vasp/vasp.xml]" - ] - }, - { - "name": "Quantum Espresso Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/quantumespresso-tests/data/parsers/quantum-espresso/W.out]" - ] - }, - { - "name": "Abinit Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/abinit-tests/data/parsers/abinit/Fe.out]" - ] - }, - { - "name": "Castep Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/castep-tests/data/parsers/castep/BC2N-Pmm2-Raman.castep]" - ] - }, - { - "name": "DL-Poly Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/dl-poly-tests/data/parsers/dl-poly/OUTPUT]" - ] - }, - { - "name": "Lib Atoms Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/lib-atoms-tests/data/parsers/lib-atoms/gp.xml]" - ] - }, - { - "name": "Octopus Normalizer", - "type": "python", - "request": "launch", - "cwd": "${workspaceFolder}", - "program": "${workspaceFolder}/.pyenv/bin/pytest", - "args": [ - "-sv", "tests/test_normalizing.py::test_normalizer[parsers/octopus-tests/data/parsers/octopus/stdout.txt]" - ] - }, - { - "name": "Python: Current File", - "type": "python", - "request": "launch", - "program": "${file}" - }, - { - "name": "Python: Attach", - "type": "python", - "request": "attach", - "localRoot": "${workspaceFolder}", - "remoteRoot": "${workspaceFolder}", - "port": 3000, - "secret": "my_secret", - "host": "localhost" - } - ] -} \ No newline at end of file diff --git a/nomad/client/local.py b/nomad/client/local.py index 1b77da741b28b78c031eab2e38cea33d5b205feb..5b15ba910d40a8114a4f6bfcbbebf8d679b0b464 100644 --- a/nomad/client/local.py +++ b/nomad/client/local.py @@ -26,7 +26,7 @@ from nomad.files import ArchiveBasedStagingUploadFiles from nomad.parsing import parser_dict, LocalBackend, match_parser from nomad.normalizing import normalizers -from .main import cli, get_nomad_url +from .main import cli class CalcProcReproduction: @@ -67,7 +67,7 @@ class CalcProcReproduction: # TODO currently only downloads mainfile self.logger.info('Downloading calc.', mainfile=self.mainfile) token = client.auth.get_user().response().result.token - req = requests.get('%s/raw/%s/%s' % (get_nomad_url(), self.upload_id, os.path.dirname(self.mainfile)) + '/*', stream=True, headers={'X-Token': token}) + req = requests.get('%s/raw/%s/%s' % (config.client.url, self.upload_id, os.path.dirname(self.mainfile)) + '/*', stream=True, headers={'X-Token': token}) with open(local_path, 'wb') as f: for chunk in req.iter_content(chunk_size=io.DEFAULT_BUFFER_SIZE): f.write(chunk) @@ -166,7 +166,7 @@ class CalcProcReproduction: def local(archive_id, show_backend=False, show_metadata=False, **kwargs): print(kwargs) utils.configure_logging() - utils.get_logger(__name__).info('Using %s' % get_nomad_url()) + utils.get_logger(__name__).info('Using %s' % config.client.url) with CalcProcReproduction(archive_id, **kwargs) as local: backend = local.parse() local.normalize_all(parser_backend=backend) diff --git a/nomad/client/main.py b/nomad/client/main.py index 4fa5a07a90f2c86820cca6d54d377fc57f33b69e..e9637c67823903128db304fe4d0c9adecdf3433f 100644 --- a/nomad/client/main.py +++ b/nomad/client/main.py @@ -24,16 +24,6 @@ from urllib.parse import urlparse from nomad import config, utils, infrastructure -_default_url = 'http://%s:%d/%s' % (config.services.api_host, config.services.api_port, config.services.api_base_path.strip('/')) -_nomad_url = os.environ.get('NOMAD_URL', _default_url) -_user = os.environ.get('NOMAD_USER', 'leonard.hofstadter@nomad-fairdi.tests.de') -_pw = os.environ.get('NOMAD_PASSWORD', 'password') - - -def get_nomad_url(): - return _nomad_url - - def create_client(): return _create_client() @@ -42,15 +32,15 @@ def _create_client(*args, **kwargs): return __create_client(*args, **kwargs) -def __create_client(user: str = _user, password: str = _pw): +def __create_client(user: str = config.client.user, password: str = config.client.password): """ A factory method to create the client. """ - host = urlparse(_nomad_url).netloc.split(':')[0] + host = urlparse(config.client.url).netloc.split(':')[0] http_client = RequestsClient() if user is not None: http_client.set_basic_auth(host, user, password) client = SwaggerClient.from_url( - '%s/swagger.json' % _nomad_url, + '%s/swagger.json' % config.client.url, http_client=http_client) utils.get_logger(__name__).info('created bravado client', user=user) @@ -65,15 +55,15 @@ def handle_common_errors(func): except requests.exceptions.ConnectionError: click.echo( '\nCould not connect to nomad at %s. ' - 'Check connection and url.' % _nomad_url) + 'Check connection and url.' % config.client.url) sys.exit(0) return wrapper @click.group() -@click.option('-n', '--url', default=_nomad_url, help='The URL where nomad is running "%s".' % _nomad_url) -@click.option('-u', '--user', default=None, help='the user name to login, default no login.') -@click.option('-w', '--password', default=_pw, help='the password use to login.') +@click.option('-n', '--url', default=config.client.url, help='The URL where nomad is running, default is "%s".' % config.client.url) +@click.option('-u', '--user', default=None, help='the user name to login, default is "%s" login.' % config.client.user) +@click.option('-w', '--password', default=config.client.password, help='the password used to login.') @click.option('-v', '--verbose', help='sets log level to info', is_flag=True) @click.option('--debug', help='sets log level to debug', is_flag=True) def cli(url: str, verbose: bool, debug: bool, user: str, password: str): @@ -92,8 +82,7 @@ def cli(url: str, verbose: bool, debug: bool, user: str, password: str): logger.info('Used nomad is %s' % url) logger.info('Used user is %s' % user) - global _nomad_url - _nomad_url = url + config.client.url = url global _create_client diff --git a/nomad/client/upload.py b/nomad/client/upload.py index fae44a59332c4c739ea05c8eb28b8e5f4e5c2c95..1b86a37fd846e2a1be22d5ae16a364e80013e8ad 100644 --- a/nomad/client/upload.py +++ b/nomad/client/upload.py @@ -19,17 +19,16 @@ import click import urllib.parse import requests -from nomad import utils +from nomad import utils, config from nomad.processing import FAILURE, SUCCESS from .main import cli, create_client -from nomad.client import main def stream_upload_with_client(client, stream, name=None): user = client.auth.get_user().response().result token = user.token - url = main._nomad_url + '/uploads/' + url = config.client.url + '/uploads/' if name is not None: url += '?name=%s' % urllib.parse.quote(name) diff --git a/nomad/config.py b/nomad/config.py index 90b19b6405b8afd5f88066c284d1546c207918b5..336830b472bbe84ca3cc4304ffe13a8f582fd590 100644 --- a/nomad/config.py +++ b/nomad/config.py @@ -12,68 +12,132 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" -This module is used to store all configuration values. It makes use of -*namedtuples* to create key sensitive configuration objects. -""" - -import os import logging -from collections import namedtuple +import os +import os.path +import yaml import warnings +from nomad import utils + warnings.filterwarnings("ignore", message="numpy.dtype size changed") warnings.filterwarnings("ignore", message="numpy.ufunc size changed") +class NomadConfig(utils.POPO): + pass + + +# class ConfigProperty: +# def __init__( +# self, name: str, default_value: Union[int, str, bool], help: str = None, +# env_var: str = None) -> None: +# self.name = name +# self.default_value = default_value, +# self.help = help +# self.env_var = env_var + CELERY_WORKER_ROUTING = 'worker' CELERY_QUEUE_ROUTING = 'queue' -CeleryConfig = namedtuple('Celery', [ - 'broker_url', 'max_memory', 'timeout', 'acks_late', 'routing']) -""" -Used to configure the RabbitMQ for celery. +rabbitmq = NomadConfig( + host='localhost', + user='rabbitmq', + password='rabbitmq' +) -Arguments: - broker_url: The rabbitmq broker URL - max_memory: Max worker memory - timeout: Task timeout - acks_late: Use celery acks_late if set - routing: Set to ``queue`` for routing via upload, calc queues. Processing tasks for one - upload might be routed to different worker and all staging files must be accessible - for all workers via distributed fs. Set to ``worker`` to route all tasks related - to the same upload to the same worker. -""" -FSConfig = namedtuple('FSConfig', ['tmp', 'staging', 'public', 'prefix_size']) -""" Used to configure file stystem access. """ +def rabbitmq_url(): + return 'pyamqp://%s:%s@%s//' % (rabbitmq.user, rabbitmq.password, rabbitmq.host) -RepositoryDBConfig = namedtuple('RepositoryDBConfig', ['host', 'port', 'dbname', 'user', 'password']) -""" Used to configure access to NOMAD-coe repository db. """ -ElasticConfig = namedtuple('ElasticConfig', ['host', 'port', 'index_name']) -""" Used to configure elastic search. """ +celery = NomadConfig( + max_memory=64e6, # 64 GB + timeout=1800, # 1/2 h + acks_late=True, + routing=CELERY_QUEUE_ROUTING +) -MongoConfig = namedtuple('MongoConfig', ['host', 'port', 'db_name']) -""" Used to configure mongo db. """ +fs = NomadConfig( + tmp='.volumes/fs/tmp', + staging='.volumes/fs/staging', + public='.volumes/fs/public', + prefix_size=2 +) -LogstashConfig = namedtuple('LogstashConfig', ['enabled', 'host', 'tcp_port', 'level']) -""" Used to configure and enable/disable the ELK based centralized logging. """ +elastic = NomadConfig( + host='localhost', + port=9200, + index_name='nomad_fairdi_calcs' +) -NomadServicesConfig = namedtuple('NomadServicesConfig', ['api_host', 'api_port', 'api_base_path', 'api_secret', 'admin_password', 'upload_url', 'disable_reset']) -""" Used to configure nomad services: worker, handler, api """ +repository_db = NomadConfig( + host='localhost', + port=5432, + dbname='nomad_fairdi_repo_db', + user='postgres', + password='nomad' +) -MailConfig = namedtuple('MailConfig', ['host', 'port', 'user', 'password', 'from_address']) -""" Used to configure how nomad can send email """ +mongo = NomadConfig( + host='localhost', + port=27017, + db_name='nomad_fairdi' +) -NormalizeConfig = namedtuple('NormalizeConfig', ['all_systems']) -""" Used to configure the normalizers """ +logstash = NomadConfig( + enabled=True, + host='localhost', + tcp_port='5000', + level=logging.DEBUG +) -rabbit_host = os.environ.get('NOMAD_RABBITMQ_HOST', 'localhost') -rabbit_port = os.environ.get('NOMAD_RABBITMQ_PORT', None) -rabbit_user = 'rabbitmq' -rabbit_password = 'rabbitmq' -rabbit_url = 'pyamqp://%s:%s@%s//' % (rabbit_user, rabbit_password, rabbit_host) +services = NomadConfig( + api_host='localhost', + api_port=8000, + api_base_path='/nomad/api', + api_secret='defaultApiSecret', + admin_password='password', + disable_reset=True +) + + +def upload_url(): + return 'http://%s:%s/%s/uploads' % (services.api_host, services.api_port, services.api_base_path[:-3]) + + +migration_source_db = NomadConfig( + host='db-repository.nomad.esc', + port=5432, + dbname='nomad_prod', + user='nomadlab', + password='*' +) + +mail = NomadConfig( + enabled=False, + with_login=False, + host='', + port=8995, + user='', + password='', + from_address='webmaster@nomad-coe.eu' +) + +normalize = NomadConfig( + all_systems=False +) + +client = NomadConfig( + user='leonard.hofstadter@nomad-fairdi.tests.de', + password='password', + url='http://localhost:8000/nomad/api' +) + +console_log_level = logging.WARNING +service = 'unknown nomad service' +release = 'devel' +auxfile_cutoff = 30 def get_loglevel_from_env(key, default_level=logging.INFO): @@ -84,78 +148,113 @@ def get_loglevel_from_env(key, default_level=logging.INFO): try: return int(plain_value) except ValueError: - return getattr(logging, plain_value, default_level) + return getattr(logging, plain_value) -celery = CeleryConfig( - broker_url=rabbit_url, - max_memory=int(os.environ.get('NOMAD_CELERY_MAXMEMORY', 64e6)), # 64 GB - timeout=int(os.environ.get('NOMAD_CELERY_TIMEOUT', 1800)), # 1/2h - acks_late=bool(os.environ.get('NOMAD_CELERY_ACKS_LATE', True)), - routing=os.environ.get('NOMAD_CELERY_ROUTING', CELERY_QUEUE_ROUTING) -) -fs = FSConfig( - tmp=os.environ.get('NOMAD_FILES_TMP_DIR', '.volumes/fs/tmp'), - staging=os.environ.get('NOMAD_FILES_STAGING_DIR', '.volumes/fs/staging'), - public=os.environ.get('NOMAD_FILES_PUBLIC_DIR', '.volumes/fs/public'), - prefix_size=int(os.environ.get('NOMAD_FILES_PREFIX_SIZE', 2)) -) -elastic = ElasticConfig( - host=os.environ.get('NOMAD_ELASTIC_HOST', 'localhost'), - port=int(os.environ.get('NOMAD_ELASTIC_PORT', 9200)), - index_name=os.environ.get('NOMAD_ELASTIC_INDEX_NAME', 'nomad_fairdi_calcs') -) -repository_db = RepositoryDBConfig( - host=os.environ.get('NOMAD_COE_REPO_DB_HOST', 'localhost'), - port=int(os.environ.get('NOMAD_COE_REPO_DB_PORT', 5432)), - dbname=os.environ.get('NOMAD_COE_REPO_DB_NAME', 'nomad_fairdi_repo_db'), - user=os.environ.get('NOMAD_COE_REPO_DB_USER', 'postgres'), - password=os.environ.get('NOMAD_COE_REPO_PASSWORD', 'nomad') -) -mongo = MongoConfig( - host=os.environ.get('NOMAD_MONGO_HOST', 'localhost'), - port=int(os.environ.get('NOMAD_MONGO_PORT', 27017)), - db_name=os.environ.get('NOMAD_MONGO_DB_NAME', 'nomad_fairdi') -) -logstash = LogstashConfig( - enabled=True, - host=os.environ.get('NOMAD_LOGSTASH_HOST', 'localhost'), - tcp_port=int(os.environ.get('NOMAD_LOGSTASH_TCPPORT', '5000')), - level=get_loglevel_from_env('NOMAD_LOGSTASH_LEVEL', default_level=logging.DEBUG) -) -services = NomadServicesConfig( - api_host=os.environ.get('NOMAD_API_HOST', 'localhost'), - api_port=int(os.environ.get('NOMAD_API_PORT', 8000)), - api_base_path=os.environ.get('NOMAD_API_BASE_PATH', '/nomad/api'), - api_secret=os.environ.get('NOMAD_API_SECRET', 'defaultApiSecret'), - admin_password=os.environ.get('NOMAD_API_ADMIN_PASSWORD', 'password'), - upload_url=os.environ.get('NOMAD_UPLOAD_URL', 'http://localhost/nomad/uploads'), - disable_reset=os.environ.get('NOMAD_API_DISABLE_RESET', 'false') == 'true' -) -migration_source_db = RepositoryDBConfig( - host=os.environ.get('NOMAD_MIGRATION_SOURCE_DB_HOST', 'db-repository.nomad.esc'), - port=int(os.environ.get('NOMAD_MIGRATION_SOURCE_DB_PORT', 5432)), - dbname=os.environ.get('NOMAD_MIGRATION_SOURCE_DB_NAME', 'nomad_prod'), - user=os.environ.get('NOMAD_MIGRATION_SOURCE_USER', 'nomadlab'), - password=os.environ.get('NOMAD_MIGRATION_SOURCE_PASSWORD', '*') -) -mail = MailConfig( - host=os.environ.get('NOMAD_SMTP_HOST', ''), # empty or None host disables email - port=int(os.environ.get('NOMAD_SMTP_PORT', 8995)), - user=os.environ.get('NOMAD_SMTP_USER', None), - password=os.environ.get('NOMAD_SMTP_PASSWORD', None), - from_address=os.environ.get('NOMAD_MAIL_FROM', 'webmaster@nomad-coe.eu') -) -normalize = NormalizeConfig( - all_systems=False -) +transformations = { + 'console_log_level': get_loglevel_from_env, + 'logstash_level': get_loglevel_from_env +} -console_log_level = get_loglevel_from_env('NOMAD_CONSOLE_LOGLEVEL', default_level=logging.WARNING) -service = os.environ.get('NOMAD_SERVICE', 'unknown nomad service') -release = os.environ.get('NOMAD_RELEASE', 'devel') -auxfile_cutoff = 30 -""" -Number of max auxfiles. More auxfiles means no auxfiles, but probably a directory of many -mainfiles. -""" +# use std python logger, since logging is not configured while loading configuration +logger = logging.getLogger(__name__) + + +def apply(config, key, value) -> None: + """ + Changes the config according to given key and value. The keys are interpreted as paths + to config values with ``_`` as a separator. E.g. ``fs_staging`` leading to + ``config.fs.staging`` + """ + + path = list(reversed(key.split('_'))) + child_segment = None + current_value = None + child_config = config + child_key = None + + try: + while len(path) > 0: + if child_segment is None: + child_segment = path.pop() + else: + child_segment += '_' + path.pop() + + if child_segment in child_config: + current_value = child_config[child_segment] + + if current_value is None: + if len(path) == 0: + raise KeyError + if isinstance(current_value, NomadConfig): + child_config = current_value + child_segment = None + else: + if len(path) > 0: + raise KeyError() + else: + child_key = child_segment + break + + if child_key is None or current_value is None: + raise KeyError() + except KeyError: + logger.error('config key %s does not exist' % key) + + if not isinstance(value, type(current_value)): + try: + value = transformations.get(key, type(current_value))(value) + except Exception as e: + logger.error( + 'config key %s value %s has wrong type: %s' % (key, str(value), str(e))) + + child_config[child_key] = value + + +def load_config(config_file: str = os.environ.get('NOMAD_CONFIG', 'nomad.yml')) -> None: + # load yml and override defaults + if os.path.exists(config_file): + with open(config_file, 'r') as stream: + try: + config_data = yaml.load(stream) + except yaml.YAMLError as e: + logger.error('cannot read nomad config', exc_info=e) + + def adapt(config, new_config, child_key=None): + for key, value in new_config.items(): + if key in config: + if child_key is None: + qualified_key = key + else: + qualified_key = '%s_%s' % (child_key, key) + + current_value = config[key] + if isinstance(value, dict) and isinstance(current_value, NomadConfig): + adapt(current_value, value, qualified_key) + else: + if not isinstance(value, type(current_value)): + try: + value = transformations.get(qualified_key, type(current_value))(value) + except Exception as e: + logger.error( + 'config key %s value %s has wrong type: %s' % (key, str(value), str(e))) + else: + config[key] = value + else: + logger.error('config key %s does not exist' % key) + + adapt(globals(), config_data) + + # load env and override yml and defaults + kwargs = { + key[len('NOMAD_'):].lower(): value + for key, value in os.environ.items() + if key.startswith('NOMAD_') + } + config = globals() + for key, value in kwargs.items(): + apply(config, key, value) + + +load_config() diff --git a/nomad/infrastructure.py b/nomad/infrastructure.py index 182bc9d72d0da25d782c4717435834706678f669..b6177dd24dda755656cfd40d4d0680e736ae51b4 100644 --- a/nomad/infrastructure.py +++ b/nomad/infrastructure.py @@ -180,7 +180,7 @@ def sqlalchemy_repository_db(exists: bool = False, readonly: bool = True, **kwar def no_flush(): pass - params = config.repository_db._asdict() + params = dict(**config.repository_db) params.update(**kwargs) url = 'postgresql://%s:%s@%s:%d/%s' % utils.to_tuple(params, 'user', 'password', 'host', 'port', 'dbname') # We tried to set a very high isolation level, to prevent conflicts between transactions on the @@ -311,15 +311,12 @@ def remove(): @contextmanager def repository_db_connection(dbname=None, with_trans=True): """ Contextmanager for a psycopg2 session for the NOMAD-coe repository postgresdb """ - repository_db_dict = config.repository_db._asdict() - if dbname is not None: - repository_db_dict.update(dbname=dbname) conn_str = "host='%s' port=%d dbname='%s' user='%s' password='%s'" % ( - repository_db_dict['host'], - repository_db_dict['port'], - repository_db_dict['dbname'], - repository_db_dict['user'], - repository_db_dict['password']) + config.repository_db.host, + config.repository_db.port, + config.repository_db.dbname if dbname is None else dbname, + config.repository_db.user, + config.repository_db.password) conn = psycopg2.connect(conn_str) if not with_trans: @@ -403,7 +400,7 @@ def reset_repository_db_content(): def send_mail(name: str, email: str, message: str, subject: str): - if config.mail.host is None or config.mail.host.strip() == '': + if not config.mail.enabled: return logger = utils.get_logger(__name__) @@ -415,15 +412,15 @@ def send_mail(name: str, email: str, message: str, subject: str): except Exception as e: logger.warning('Could use TTS', exc_info=e) - if config.mail.user is not None: + if config.mail.with_login: try: - server.login("youremailusername", "password") + server.login(config.mail.user, config.mail.password) except Exception as e: logger.warning('Could not log into mail server', exc_info=e) msg = MIMEText(message) msg['Subject'] = subject - msg['From'] = 'nomad@fairdi webmaster' + msg['From'] = config.mail.from_address msg['To'] = name try: diff --git a/nomad/processing/base.py b/nomad/processing/base.py index 6fdab84ca940deebb1cfaacf22242754393eff5a..bf7aa238a75c1e61a38cb9688353b5706a3eb66b 100644 --- a/nomad/processing/base.py +++ b/nomad/processing/base.py @@ -48,7 +48,7 @@ def setup(**kwargs): 'celery configured with acks_late=%s' % str(config.celery.acks_late)) -app = Celery('nomad.processing', broker=config.celery.broker_url) +app = Celery('nomad.processing', broker=config.rabbitmq_url()) app.conf.update(worker_hijack_root_logger=False) app.conf.update(worker_max_memory_per_child=config.celery.max_memory) if config.celery.routing == config.CELERY_WORKER_ROUTING: diff --git a/nomad/utils.py b/nomad/utils.py index b3a1e4cb0689076ff6964ced0094c0a518928c9f..ec0eeb5cb93e99eb372469485e344258175c020f 100644 --- a/nomad/utils.py +++ b/nomad/utils.py @@ -48,8 +48,6 @@ import re from werkzeug.exceptions import HTTPException import hashlib -from nomad import config - default_hash_len = 28 """ Length of hashes and hash-based ids (e.g. calc, upload) in nomad. """ @@ -166,6 +164,7 @@ class LogstashFormatter(logstash.formatter.LogstashFormatterBase): message[key] = value else: + from nomad import config structlog['nomad.service'] = config.service structlog['nomad.release'] = config.release message.update(structlog) @@ -213,6 +212,7 @@ def add_logstash_handler(logger): if isinstance(handler, LogstashHandler)), None) if logstash_handler is None: + from nomad import config logstash_handler = LogstashHandler( config.logstash.host, config.logstash.tcp_port, version=1) @@ -222,6 +222,8 @@ def add_logstash_handler(logger): def configure_logging(): + from nomad import config + # configure structlog log_processors = [ StackInfoRenderer(), @@ -275,6 +277,8 @@ def get_logger(name, **kwargs): Returns a structlog logger that is already attached with a logstash handler. Use additional *kwargs* to pre-bind some values to all events. """ + from nomad import config + if name.startswith('nomad.'): name = '.'.join(name.split('.')[:2]) diff --git a/ops/docker-compose/nomad/docker-compose.develk.yml b/ops/docker-compose/nomad/docker-compose.develk.yml index 9bf20e8a5958962c176b7e9b5f335ada1d0d5f9f..2acc7b9486c2cef1d0e88721e7d05d852e110b25 100644 --- a/ops/docker-compose/nomad/docker-compose.develk.yml +++ b/ops/docker-compose/nomad/docker-compose.develk.yml @@ -52,7 +52,7 @@ services: image: nomad/backend environment: NOMAD_LOGSTASH_LEVEL: DEBUG - NOMAD_CONSOLE_LOGLEVEL: INFO + NOMAD_CONSOLE_LOG_LEVEL: INFO links: - elk @@ -62,7 +62,7 @@ services: image: nomad/backend environment: NOMAD_LOGSTASH_LEVEL: DEBUG - NOMAD_CONSOLE_LOGLEVEL: INFO + NOMAD_CONSOLE_LOG_LEVEL: INFO depends_on: - worker ports: diff --git a/ops/docker-compose/nomad/docker-compose.yml b/ops/docker-compose/nomad/docker-compose.yml index b050044fcc85de8447c0cb62359571a858a9ce78..ae68974fdc546716edacefff318c1c7f3929372a 100644 --- a/ops/docker-compose/nomad/docker-compose.yml +++ b/ops/docker-compose/nomad/docker-compose.yml @@ -19,8 +19,6 @@ x-common-variables: &nomad_backend_env NOMAD_LOGSTASH_HOST: elk NOMAD_ELASTIC_HOST: elastic NOMAD_MONGO_HOST: mongo - NOMAD_SMTP_HOST: '' - NOMAD_UPLOAD_URL: '' services: # postgres for NOMAD-coe repository API and GUI @@ -92,11 +90,11 @@ services: container_name: nomad_api environment: <<: *nomad_backend_env - NOMAD_API_BASE_PATH: /nomad/api - NOMAD_API_HOST: ${EXTERNAL_HOST} - NOMAD_API_PORT: ${EXTERNAL_PORT} + NOMAD_SERVICES_API_BASE_PATH: /nomad/api + NOMAD_SERVICES_API_HOST: ${EXTERNAL_HOST} + NOMAD_SERVICES_API_PORT: ${EXTERNAL_PORT} + NOMAD_SERVICES_API_SECRET: ${API_SECRET} NOMAD_SERVICE: nomad_api - NOMAD_API_SECRET: ${API_SECRET} links: - postgres - rabbitmq diff --git a/ops/helm/nomad-full/.gitignore b/ops/helm/nomad-full/.gitignore deleted file mode 100644 index 711a39c541afc04f86b51e268b350d204f48c875..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/.gitignore +++ /dev/null @@ -1 +0,0 @@ -charts/ \ No newline at end of file diff --git a/ops/helm/nomad-full/.helmignore b/ops/helm/nomad-full/.helmignore deleted file mode 100644 index 50af0317254197a5a019f4ac2f8ecc223f93f5a7..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/ops/helm/nomad-full/Chart.yaml b/ops/helm/nomad-full/Chart.yaml deleted file mode 100644 index 9796ee1645c38db0a0906b5ea7cb91f7c2248bb3..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes that runs the whole nomad including databases. -name: nomad-full -version: 0.1.0 diff --git a/ops/helm/nomad-full/requirements.lock b/ops/helm/nomad-full/requirements.lock deleted file mode 100644 index 9cc3a25afa8d9a24b953f82908e02f772e645911..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/requirements.lock +++ /dev/null @@ -1,15 +0,0 @@ -dependencies: -- name: rabbitmq - repository: https://kubernetes-charts.storage.googleapis.com/ - version: 4.0.1 -- name: mongodb - repository: https://kubernetes-charts.storage.googleapis.com/ - version: 4.9.1 -- name: elasticsearch - repository: https://kubernetes-charts.storage.googleapis.com/ - version: 1.15.0 -- name: postgresql - repository: https://kubernetes-charts.storage.googleapis.com/ - version: 3.1.1 -digest: sha256:c6c65e79414429b8b2ecdba6fa2f13628614a0be3c5fff8947c79270a00386fb -generated: 2018-12-12T14:02:40.726497+01:00 diff --git a/ops/helm/nomad-full/requirements.yaml b/ops/helm/nomad-full/requirements.yaml deleted file mode 100644 index a4f11fa4ddc24477738e29a7cf681dc60b854055..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/requirements.yaml +++ /dev/null @@ -1,13 +0,0 @@ -dependencies: -- name: rabbitmq - version: "4.0.1" - repository: "https://kubernetes-charts.storage.googleapis.com/" -- name: mongodb - version: "4.9.1" - repository: "https://kubernetes-charts.storage.googleapis.com/" -- name: elasticsearch - version: "1.15.0" - repository: "https://kubernetes-charts.storage.googleapis.com/" -- name: postgresql - version: "3.1.1" - repository: "https://kubernetes-charts.storage.googleapis.com/" \ No newline at end of file diff --git a/ops/helm/nomad-full/templates/_helpers.tpl b/ops/helm/nomad-full/templates/_helpers.tpl deleted file mode 100644 index 1d5277e45c79510691d3311e97bea01702d3f5bd..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "nomad.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "nomad.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "nomad.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/ops/helm/nomad-full/templates/api-deployment.yaml b/ops/helm/nomad-full/templates/api-deployment.yaml deleted file mode 100644 index 82f5a412eaee0cb90ad52cc29b28e582721e8f86..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/templates/api-deployment.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nomad.fullname" . }}-api - labels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-api - helm.sh/chart: {{ include "nomad.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.api.replicas }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-api - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-api - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - containers: - - name: {{ include "nomad.name" . }}-api - image: "{{ .Values.images.nomad.name }}:{{ .Values.images.nomad.tag }}" - volumeMounts: - - mountPath: /app/.volumes/fs - name: files-volume - env: - - name: NOMAD_SERVICE - value: "api" - - name: NOMAD_LOGSTASH_HOST - value: {{ .Values.logstash.host }} - - name: NOMAD_LOGSTASH_TCPPORT - value: {{ .Values.logstash.port }} - - name: NOMAD_CONSOLE_LOGLEVEL - value: {{ .Values.api.console_loglevel }} - - name: NOMAD_LOGSTASH_LEVEL - value: {{ .Values.api.logstash_loglevel }} - - name: NOMAD_API_PORT - value: {{ .Values.api.port }} - - name: NOMAD_API_SECRET - value: {{ .Values.api.secret }} - imagePullSecrets: - - name: {{ .Values.images.secret }} - volumes: - - name: files-volume - hostPath: - path: {{ .Values.volumes.files }} - type: Directory diff --git a/ops/helm/nomad-full/templates/api-service.yaml b/ops/helm/nomad-full/templates/api-service.yaml deleted file mode 100644 index 0a7cb7ac73558caeee79043761028fe59de055c1..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/templates/api-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nomad.fullname" . }}-api - labels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-api - helm.sh/chart: {{ include "nomad.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - type: {{ .Values.api.service_type }} - ports: - - port: {{ .Values.api.port }} - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{ include "nomad.name" . }}-api - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/ops/helm/nomad-full/templates/worker-deployment.yaml b/ops/helm/nomad-full/templates/worker-deployment.yaml deleted file mode 100644 index 6d9a1aaf6bb45d04735b5d077879b1b9f73addb7..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/templates/worker-deployment.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nomad.fullname" . }}-worker - labels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-worker - helm.sh/chart: {{ include "nomad.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.worker.replicas }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-worker - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "nomad.name" . }}-worker - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - containers: - - name: {{ include "nomad.name" . }}-worker - image: "{{ .Values.images.nomad.name }}:{{ .Values.images.nomad.tag }}" - volumeMounts: - - mountPath: /app/.volumes/fs - name: files-volume - env: - - name: NOMAD_SERVICE - value: "worker" - - name: NOMAD_LOGSTASH_HOST - value: {{ .Values.logstash.host }} - - name: NOMAD_LOGSTASH_TCPPORT - value: {{ .Values.logstash.port }} - - name: NOMAD_CONSOLE_LOGLEVEL - value: {{ .Values.worker.console_loglevel }} - - name: NOMAD_LOGSTASH_LEVEL - value: {{ .Values.worker.logstash_loglevel }} - imagePullSecrets: - - name: {{ .Values.images.secret }} - volumes: - - name: files-volume - hostPath: - path: {{ .Values.volumes.files }} - type: Directory diff --git a/ops/helm/nomad-full/values.yaml b/ops/helm/nomad-full/values.yaml deleted file mode 100644 index fafac326be16cb17c94ab85fdd77d92d20e97980..0000000000000000000000000000000000000000 --- a/ops/helm/nomad-full/values.yaml +++ /dev/null @@ -1,69 +0,0 @@ -## Default values for nomad@FAIRDI - -## Everything concerning the container images to be used -images: - ## The kubernetes docker-registry secret that can be used to access the registry - # with the container image in it. - # It can be created via: - # kubectl create secret docker-registry gitlab-mpcdf --docker-server=gitlab-registry.mpcdf.mpg.de --docker-username=<your-user-name > --docker-password=<yourpass> --docker-email=<email> - secret: gitlab-mpcdf - - ## The nomad image with all nomad relavant python code. Used by api and worker service. - nomad: - ## The docker container image name without tag - name: gitlab-registry.mpcdf.mpg.de/nomad-lab/nomad-fair - ## The docker container image tag - tag: latest - pullPolicy: IfNotPresent - -## Everthing concerning the nomad api -api: - replicas: 1 - port: 8000 - console_loglevel: INFO - logstash_loglevel: INFO - service_type: ClusterIP - ## Secret used as cryptographic seed - secret: "defaultApiSecret" - -## Everthing concerning the nomad worker -worker: - replicas: 2 - console_loglevel: INFO - logstash_loglevel: INFO - -rabbitmq: - image.pullSecrets: nil - rbacEnabled: false - rabbitmq: - username: rabbitmq - password: rabbitmq - erlangCookie: SWQOKODSQALRPCLNMEQG - persistence.enabled: false - securityContext.enabled: false - -mongodb: - image.pullSecrets: nil - usePassword: false - persistence.enabled: true - securityContext.enabled: false - -elasticsearch: - client.replicas: 2 - master.replicas: 1 - data.replicas: 1 - cluster.env: {MINIMUM_MASTER_NODES=""1"} - -postgresql: - postgresqlUsername: "postgres" - postgresqlPassword: "nomad" - postgresqlDatabase: "nomad" - securityContext.enabled: false - -logstash: - port: 5000 - host: "enc-preprocessing-nomad.esc" - -## Everthing concerning the data that is used by the service -volumes: - files: /nomad/nomadlab/nomad-FAIRDI/files diff --git a/ops/helm/nomad/templates/api-deployment.yaml b/ops/helm/nomad/templates/api-deployment.yaml index 64f0d1b08f98d673358cc6b8e8a4fbd55c6f5be1..1640e3f468b13bf7ea9131d1f0d3df8bca100dd2 100644 --- a/ops/helm/nomad/templates/api-deployment.yaml +++ b/ops/helm/nomad/templates/api-deployment.yaml @@ -23,63 +23,21 @@ spec: - name: {{ include "nomad.name" . }}-api image: "{{ .Values.images.nomad.name }}:{{ .Values.images.nomad.tag }}" volumeMounts: + - mountPath: /app + name: nomad-conf - mountPath: /app/.volumes/fs/public name: public-volume - mountPath: /app/.volumes/fs/staging name: staging-volume - mountPath: /nomad name: nomad-volume - env: - - name: NOMAD_FILES_TMP_DIR - value: "{{ .Values.volumes.tmp }}" + env: - name: NOMAD_SERVICE value: "api" - - name: NOMAD_RELEASE - value: "{{ .Release.Name }}" - - name: NOMAD_LOGSTASH_HOST - value: "{{ .Values.logstash.host }}" - - name: NOMAD_LOGSTASH_TCPPORT - value: "{{ .Values.logstash.port }}" - name: NOMAD_CONSOLE_LOGLEVEL value: "{{ .Values.api.console_loglevel }}" - name: NOMAD_LOGSTASH_LEVEL value: "{{ .Values.api.logstash_loglevel }}" - - name: NOMAD_API_HOST - value: "{{ .Values.proxy.external.host }}" - - name: NOMAD_API_PORT - value: "{{ .Values.proxy.external.port }}" - - name: NOMAD_API_BASE_PATH - value: "{{ .Values.proxy.external.path }}/api" - - name: NOMAD_API_SECRET - value: "{{ .Values.api.secret }}" - - name: NOMAD_API_ADMIN_PASSWORD - value: "{{ .Values.api.adminPassword }}" - - name: NOMAD_API_DISABLE_RESET - value: "{{ .Values.api.disableReset }}" - - name: NOMAD_RABBITMQ_HOST - value: "{{ .Release.Name }}-rabbitmq" - - name: NOMAD_ELASTIC_HOST - value: "{{ .Values.elastic.host }}" - - name: NOMAD_ELASTIC_PORT - value: "{{ .Values.elastic.port }}" - - name: NOMAD_ELASTIC_INDEX_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_MONGO_HOST - value: "{{ .Values.mongo.host }}" - - name: NOMAD_MONGO_PORT - value: "{{ .Values.mongo.port }}" - - name: NOMAD_MONGO_DB_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_COE_REPO_DB_HOST - value: "{{ .Values.postgres.host }}" - - name: NOMAD_COE_REPO_DB_PORT - value: "{{ .Values.postgres.port }}" - - name: NOMAD_COE_REPO_DB_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_CELERY_ROUTING - value: "{{ .Values.worker.routing }}" - - name: NOMAD_FILES_PREFIX_SIZE - value: "{{ .Values.volumes.prefixSize }}" command: ["python", "-m", "gunicorn.app.wsgiapp", "--timeout", "3600", "--log-config", "ops/gunicorn.log.conf", "-w", "{{ .Values.api.worker }}", "-b 0.0.0.0:8000", "nomad.api:app"] livenessProbe: httpGet: @@ -99,6 +57,12 @@ spec: - name: {{ .Values.images.secret }} imagePullPolicy: always volumes: + - name: nomad-conf + configMap: + name: {{ include "nomad.fullname" . }}-configmap + items: + - key: nomad.yml + path: nomad.yml - name: public-volume hostPath: path: {{ .Values.volumes.public }} diff --git a/ops/helm/nomad/templates/nomad-configmap.yml b/ops/helm/nomad/templates/nomad-configmap.yml new file mode 100644 index 0000000000000000000000000000000000000000..afb07e1da34df2d0f1b463e44b3b42ddd78be9c9 --- /dev/null +++ b/ops/helm/nomad/templates/nomad-configmap.yml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "nomad.fullname" . }}-configmap + labels: + app.kubernetes.io/name: {{ include "nomad.name" . }}-configmap + helm.sh/chart: {{ include "nomad.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +data: + nomad.yml: / + service: api + api_host: + release: "{{ .Release.Name }}" + fs: + tmp: "{{ .Values.volumes.tmp }}" + prefix_size: {{ .Values.volumes.prefixSize }} + logstash: + enabled: true + host: "{{ .Values.logstash.host }}" + tcpport: "{{ .Values.logstash.port }}" + level: info + console_log_level: debug + services: + api_host: "{{ .Values.proxy.external.host }}" + api_port: "{{ .Values.proxy.external.port }}" + api_base_path: "{{ .Values.proxy.external.path }}/api" + api_secret: "{{ .Values.api.secret }}" + admin_password: "{{ .Values.api.adminPassword }}" + disable_reset: {{ .Values.api.disableReset }} + rabbitmq: + host: "{{ .Release.Name }}-rabbitmq" + elastic: + host: "{{ .Values.elastic.host }}" + port: {{ .Values.elastic.port }} + index_name: "{{ .Values.dbname }}" + mongo: + host: "{{ .Values.mongo.host }}" + port: {{ .Values.mongo.port }} + db_name: "{{ .Values.dbname }}" + repository_db: + host: "{{ .Values.postgres.host }}" + port: {{ .Values.postgres.port }} + dbname: "{{ .Values.dbname }}" + mail: + host: "{{ .Values.mail.host }}" + port: {{ .Values.mail.port }} + user: "{{ .Values.mail.user }}" + password: "{{ .Values.mail.password }}" + from_address: "{{ .Values.mail.from }}" + celery_routing: "{{ .Values.worker.routing }}" \ No newline at end of file diff --git a/ops/helm/nomad/templates/worker-deployment.yaml b/ops/helm/nomad/templates/worker-deployment.yaml index c24b5047a146813797937da8d71b280ca7735ab6..1c47b9733db16cc9e89d8e58765ab687f3ae2a1d 100644 --- a/ops/helm/nomad/templates/worker-deployment.yaml +++ b/ops/helm/nomad/templates/worker-deployment.yaml @@ -28,6 +28,8 @@ spec: requests: memory: "{{ .Values.worker.memrequest }}Gi" volumeMounts: + - mountPath: /app + name: nomad-conf - mountPath: /app/.volumes/fs/public name: public-volume - mountPath: /app/.volumes/fs/staging @@ -35,58 +37,12 @@ spec: - mountPath: /nomad name: nomad-volume env: - - name: NOMAD_FILES_TMP_DIR - value: "{{ .Values.volumes.tmp }}" - name: NOMAD_SERVICE value: "worker" - - name: NOMAD_RELEASE - value: "{{ .Release.Name }}" - - name: NOMAD_LOGSTASH_HOST - value: "{{ .Values.logstash.host }}" - - name: NOMAD_LOGSTASH_TCPPORT - value: "{{ .Values.logstash.port }}" - name: NOMAD_CONSOLE_LOGLEVEL value: "{{ .Values.worker.console_loglevel }}" - name: NOMAD_LOGSTASH_LEVEL value: "{{ .Values.worker.logstash_loglevel }}" - - name: NOMAD_RABBITMQ_HOST - value: "{{ .Release.Name }}-rabbitmq" - - name: NOMAD_ELASTIC_HOST - value: "{{ .Values.elastic.host }}" - - name: NOMAD_ELASTIC_PORT - value: "{{ .Values.elastic.port }}" - - name: NOMAD_ELASTIC_INDEX_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_MONGO_HOST - value: "{{ .Values.mongo.host }}" - - name: NOMAD_MONGO_PORT - value: "{{ .Values.mongo.port }}" - - name: NOMAD_MONGO_DB_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_COE_REPO_DB_HOST - value: "{{ .Values.postgres.host }}" - - name: NOMAD_COE_REPO_DB_PORT - value: "{{ .Values.postgres.port }}" - - name: NOMAD_COE_REPO_DB_NAME - value: "{{ .Values.dbname }}" - - name: NOMAD_UPLOAD_URL - value: "{{ .Values.uploadurl }}" - - name: NOMAD_SMTP_HOST - value: "{{ .Values.mail.host }}" - - name: NOMAD_SMTP_PORT - value: "{{ .Values.mail.port }}" - - name: NOMAD_SMTP_USER - value: "{{ .Values.mail.user }}" - - name: NOMAD_SMTP_PASSWORD - value: "{{ .Values.mail.password }}" - - name: NOMAD_MAIL_FROM - value: "{{ .Values.mail.from }}" - - name: NOMAD_CONSOLE_LOGLEVEL - value: "ERROR" - - name: NOMAD_CELERY_ROUTING - value: "{{ .Values.worker.routing }}" - - name: NOMAD_FILES_PREFIX_SIZE - value: "{{ .Values.volumes.prefixSize }}" command: ["python", "-m", "celery", "worker", "-A", "nomad.processing", "-Q", "celery,calcs,uploads"] livenessProbe: exec: @@ -110,6 +66,12 @@ spec: - name: {{ .Values.images.secret }} imagePullPolicy: always volumes: + - name: nomad-conf + configMap: + name: {{ include "nomad.fullname" . }}-configmap + items: + - key: nomad.yml + path: nomad.yml - name: public-volume hostPath: path: {{ .Values.volumes.public }} diff --git a/ops/scripts/migration.env.sh b/ops/scripts/migration.env.sh new file mode 100644 index 0000000000000000000000000000000000000000..c33bf76957a9edea6ebebf41824b89018d16f6a8 --- /dev/null +++ b/ops/scripts/migration.env.sh @@ -0,0 +1,4 @@ +export NOMAD_CLIENT_URL=http://enc-staging-nomad.esc.rzg.mpg.de/fairdi/nomad/migration/api +export NOMAD_CLIENT_USER=admin +export NOMAD_FS_TMP=/nomad/mscheidg/migration_tmp +export NOMAD_LOGSTASH_TCP_PORT=15000 diff --git a/ops/scripts/migration_client.env.sh b/ops/scripts/migration_client.env.sh deleted file mode 100644 index 54cdb85d9f9944b1ddd1c110f8a19a734175b4a5..0000000000000000000000000000000000000000 --- a/ops/scripts/migration_client.env.sh +++ /dev/null @@ -1,4 +0,0 @@ -export NOMAD_URL=http://enc-staging-nomad.esc.rzg.mpg.de/fairdi/nomad/migration/api -export NOMAD_USER=admin -export NOMAD_FILES_NOMAD_TMP_DIR=/nomad/mscheidg/migration_tmp -export NOMAD_LOGSTASH_TCPPORT=15000 diff --git a/ops/scripts/migration_env.sh b/ops/scripts/migration_env.sh deleted file mode 100644 index 68546852928433eaa987268520e850c58075401e..0000000000000000000000000000000000000000 --- a/ops/scripts/migration_env.sh +++ /dev/null @@ -1,13 +0,0 @@ -export NOMAD_URL=http://enc-staging-nomad.esc.rzg.mpg.de/fairdi/nomad/migration/api -export NOMAD_USER=admin -export NOMAD_FILES_NOMAD_TMP_DIR=/nomad/mscheidg/migration_tmp -export NOMAD_LOGSTASH_TCPPORT=15000 - -export NOMAD_FILES_TMP_DIR=/scratch/nomad-fair/local/tmp -export NOMAD_FILES_OBJECT_DIR=/scratch/nomad-fair/local/objects -export NOMAD_FILES_NOMAD_TMP=/nomad/mscheidg/migration_tmp -export NOMAD_ELASTIC_PORT=19200 -export NOMAD_ELASTIC_INDEX_NAME=fairdi_nomad_local -export NOMAD_MONGO_PORT=37017 -export NOMAD_MONGO_DB_NAME=fairdi_nomad_local -export NOMAD_COE_REPO_DB_NAME=fairdi_nomad_local diff --git a/tests/__init__.py b/tests/__init__.py index 482bfbc29ce9e523dcf01ee52fceee554466750e..e48f987ae9f076668b44484fd972c9599a585b40 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -30,11 +30,7 @@ from nomad import config # changing them afterwards does not change anything anymore. # For convinience we test the api without path prefix. -services_config = config.services._asdict() -services_config.update(api_base_path='') -config.services = config.NomadServicesConfig(**services_config) +config.services.api_base_path = '' # We use a mocked in memory mongo version. -mongo_config = config.mongo._asdict() -mongo_config.update(host='mongomock://localhost') -config.mongo = config.MongoConfig(**mongo_config) +config.mongo.host = 'mongomock://localhost' diff --git a/tests/conftest.py b/tests/conftest.py index 05ba2b02c654083a713b65a0faaaf54945b64c26..b0667d4d0ad8705574460cb34587d96d7b64c843 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,16 +50,17 @@ def monkeysession(request): @pytest.fixture(scope='session', autouse=True) def nomad_logging(): - config.logstash = config.logstash._replace(enabled=False) + config.logstash.enabled = False config.console_log_level = test_log_level infrastructure.setup_logging() @pytest.fixture(scope='session', autouse=True) -def raw_files_infra(monkeysession): - monkeysession.setattr('nomad.config.fs', config.FSConfig( - tmp='.volumes/test_fs/tmp', staging='.volumes/test_fs/staging', - public='.volumes/test_fs/public', prefix_size=2)) +def raw_files_infra(): + config.fs.tmp = '.volumes/test_fs/tmp' + config.fs.staging = '.volumes/test_fs/staging' + config.fs.public = '.volumes/test_fs/public' + config.fs.prefix_size = 2 @pytest.fixture(scope='function') @@ -80,7 +81,7 @@ def raw_files(raw_files_infra): @pytest.fixture(scope='function') -def client(monkeysession, mongo): +def client(mongo): api.app.config['TESTING'] = True client = api.app.test_client() @@ -95,7 +96,7 @@ def celery_includes(): @pytest.fixture(scope='session') def celery_config(): return { - 'broker_url': config.celery.broker_url + 'broker_url': config.rabbitmq_url() } @@ -155,7 +156,7 @@ def worker(mongo, celery_session_worker, celery_inspect): @pytest.fixture(scope='session') -def mongo_infra(monkeysession): +def mongo_infra(): return infrastructure.setup_mongo() @@ -167,9 +168,9 @@ def mongo(mongo_infra): @pytest.fixture(scope='session') -def elastic_infra(monkeysession): +def elastic_infra(): """ Provides elastic infrastructure to the session """ - monkeysession.setattr('nomad.config.elastic', config.elastic._replace(index_name='test_nomad_fairdi_calcs')) + config.elastic.index_name = 'test_nomad_fairdi_calcs' try: return infrastructure.setup_elastic() except Exception: @@ -201,7 +202,7 @@ def elastic(elastic_infra): @contextmanager -def create_postgres_infra(monkeysession=None, **kwargs): +def create_postgres_infra(patch=None, **kwargs): """ A generator that sets up and tears down a test db and monkeypatches it to the respective global infrastructure variables. @@ -210,15 +211,11 @@ def create_postgres_infra(monkeysession=None, **kwargs): db_args.update(**kwargs) old_config = config.repository_db - new_config = config.RepositoryDBConfig( - old_config.host, - old_config.port, - db_args.get('dbname'), - old_config.user, - old_config.password) + new_config = config.NomadConfig(**config.repository_db) + new_config.update(**db_args) - if monkeysession is not None: - monkeysession.setattr('nomad.config.repository_db', new_config) + if patch is not None: + patch.setattr('nomad.config.repository_db', new_config) connection, _ = infrastructure.sqlalchemy_repository_db(**db_args) assert connection is not None @@ -229,18 +226,18 @@ def create_postgres_infra(monkeysession=None, **kwargs): db = Session(bind=connection, autocommit=True) old_connection, old_db = None, None - if monkeysession is not None: + if patch is not None: from nomad.infrastructure import repository_db_conn, repository_db old_connection, old_db = repository_db_conn, repository_db - monkeysession.setattr('nomad.infrastructure.repository_db_conn', connection) - monkeysession.setattr('nomad.infrastructure.repository_db', db) + patch.setattr('nomad.infrastructure.repository_db_conn', connection) + patch.setattr('nomad.infrastructure.repository_db', db) yield db - if monkeysession is not None: - monkeysession.setattr('nomad.infrastructure.repository_db_conn', old_connection) - monkeysession.setattr('nomad.infrastructure.repository_db', old_db) - monkeysession.setattr('nomad.config.repository_db', old_config) + if patch is not None: + patch.setattr('nomad.infrastructure.repository_db_conn', old_connection) + patch.setattr('nomad.infrastructure.repository_db', old_db) + patch.setattr('nomad.config.repository_db', old_config) trans.rollback() db.expunge_all() @@ -461,16 +458,8 @@ def smtpd(request): @pytest.fixture(scope='function', autouse=True) def mails(smtpd, monkeypatch): smtpd.clear() - - old_config = config.mail - new_config = config.MailConfig( - 'localhost', - old_config.port, - old_config.user, - old_config.password, - old_config.from_address) - - monkeypatch.setattr('nomad.config.mail', new_config) + monkeypatch.setattr('nomad.config.mail.enabled', True) + monkeypatch.setattr('nomad.config.mail.host', 'localhost') yield smtpd diff --git a/tests/processing/test_data.py b/tests/processing/test_data.py index badb9277a6be11270f40bb0062d4fb3890f52e7a..6341b1cc1606aab1797fd130cc1c12d244b5518d 100644 --- a/tests/processing/test_data.py +++ b/tests/processing/test_data.py @@ -25,7 +25,8 @@ from nomad.processing import Upload, Calc from nomad.processing.base import task as task_decorator, FAILURE, SUCCESS -def test_send_mail(mails): +def test_send_mail(mails, monkeypatch): + monkeypatch.setattr('nomad.config.mail.enabled', True) infrastructure.send_mail('test name', 'test@email.de', 'test message', 'subjct') for message in mails.messages: @@ -90,7 +91,7 @@ def assert_processing(upload: Upload): assert upload_files.metadata.get(calc.calc_id) is not None -def test_processing(processed, no_warn, mails): +def test_processing(processed, no_warn, mails, monkeypatch): assert_processing(processed) assert len(mails.messages) == 1 diff --git a/tests/test_api.py b/tests/test_api.py index 79589d9055fe052cb886305df8e05e703cf75077..2191667ccc407251f5d340ec6f456c809b22cc2c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -22,7 +22,7 @@ import inspect from passlib.hash import bcrypt from datetime import datetime -from nomad import config, coe_repo, search, parsing, files +from nomad import coe_repo, search, parsing, files from nomad.files import UploadFiles, PublicUploadFiles from nomad.processing import Upload, Calc, SUCCESS @@ -48,12 +48,14 @@ def test_user_signature_token(client, test_user_auth): class TestAdmin: @pytest.mark.timeout(10) - def test_reset(self, client, admin_user_auth, expandable_postgres): + def test_reset(self, client, admin_user_auth, expandable_postgres, monkeypatch): + monkeypatch.setattr('nomad.config.services.disable_reset', False) rv = client.post('/admin/reset', headers=admin_user_auth) assert rv.status_code == 200 @pytest.mark.timeout(10) - def test_remove(self, client, admin_user_auth, expandable_postgres): + def test_remove(self, client, admin_user_auth, expandable_postgres, monkeypatch): + monkeypatch.setattr('nomad.config.services.disable_reset', False) rv = client.post('/admin/remove', headers=admin_user_auth) assert rv.status_code == 200 @@ -65,22 +67,7 @@ class TestAdmin: rv = client.post('/admin/reset', headers=test_user_auth) assert rv.status_code == 401 - @pytest.fixture(scope='function') - def disable_reset(self, monkeypatch): - old_config = config.services - new_config = config.NomadServicesConfig( - config.services.api_host, - config.services.api_port, - config.services.api_base_path, - config.services.api_secret, - config.services.admin_password, - config.services.upload_url, - True) - monkeypatch.setattr(config, 'services', new_config) - yield None - monkeypatch.setattr(config, 'services', old_config) - - def test_disabled(self, client, admin_user_auth, disable_reset, postgres): + def test_disabled(self, client, admin_user_auth, postgres): rv = client.post('/admin/reset', headers=admin_user_auth) assert rv.status_code == 400