mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 04:12:45 +00:00 
			
		
		
		
	Change the APScheduler Logic and try to catch more errors in Daemon
This commit is contained in:
		@@ -33,7 +33,7 @@ def create_app(config: Config = Config) -> Flask:
 | 
				
			|||||||
    app.config.from_object(config)
 | 
					    app.config.from_object(config)
 | 
				
			||||||
    config.init_app(app)
 | 
					    config.init_app(app)
 | 
				
			||||||
    docker_client.login(
 | 
					    docker_client.login(
 | 
				
			||||||
        username=app.config['NOPAQUE_DOCKER_REGISTRY_USERNAME'],
 | 
					        app.config['NOPAQUE_DOCKER_REGISTRY_USERNAME'],
 | 
				
			||||||
        password=app.config['NOPAQUE_DOCKER_REGISTRY_PASSWORD'],
 | 
					        password=app.config['NOPAQUE_DOCKER_REGISTRY_PASSWORD'],
 | 
				
			||||||
        registry=app.config['NOPAQUE_DOCKER_REGISTRY']
 | 
					        registry=app.config['NOPAQUE_DOCKER_REGISTRY']
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@@ -48,9 +48,6 @@ def create_app(config: Config = Config) -> Flask:
 | 
				
			|||||||
    scheduler.init_app(app)
 | 
					    scheduler.init_app(app)
 | 
				
			||||||
    socketio.init_app(app, message_queue=app.config['NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI'])  # noqa
 | 
					    socketio.init_app(app, message_queue=app.config['NOPAQUE_SOCKETIO_MESSAGE_QUEUE_URI'])  # noqa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    from . import tasks
 | 
					 | 
				
			||||||
    tasks.register(app, scheduler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from .admin import bp as admin_blueprint
 | 
					    from .admin import bp as admin_blueprint
 | 
				
			||||||
    app.register_blueprint(admin_blueprint, url_prefix='/admin')
 | 
					    app.register_blueprint(admin_blueprint, url_prefix='/admin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,4 +78,7 @@ def create_app(config: Config = Config) -> Flask:
 | 
				
			|||||||
    from .settings import bp as settings_blueprint
 | 
					    from .settings import bp as settings_blueprint
 | 
				
			||||||
    app.register_blueprint(settings_blueprint, url_prefix='/settings')
 | 
					    app.register_blueprint(settings_blueprint, url_prefix='/settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from .users import bp as users_blueprint
 | 
				
			||||||
 | 
					    app.register_blueprint(users_blueprint, url_prefix='/users')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return app
 | 
					    return app
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
from app import db
 | 
					from app import db
 | 
				
			||||||
 | 
					from flask import Flask
 | 
				
			||||||
from .corpus_utils import check_corpora
 | 
					from .corpus_utils import check_corpora
 | 
				
			||||||
from .job_utils import check_jobs
 | 
					from .job_utils import check_jobs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def daemon():
 | 
					def daemon(app: Flask):
 | 
				
			||||||
    check_corpora()
 | 
					    with app.app_context():
 | 
				
			||||||
    check_jobs()
 | 
					        check_corpora()
 | 
				
			||||||
    db.session.commit()
 | 
					        check_jobs()
 | 
				
			||||||
 | 
					        db.session.commit()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,11 +90,8 @@ def _create_build_corpus_service(corpus):
 | 
				
			|||||||
            restart_policy=restart_policy,
 | 
					            restart_policy=restart_policy,
 | 
				
			||||||
            user='0:0'
 | 
					            user='0:0'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Create service "{name}" failed: {e}')
 | 
				
			||||||
            f'Create service "{name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    corpus.status = CorpusStatus.QUEUED
 | 
					    corpus.status = CorpusStatus.QUEUED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -103,17 +100,11 @@ def _checkout_build_corpus_service(corpus):
 | 
				
			|||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service = docker_client.services.get(service_name)
 | 
					        service = docker_client.services.get(service_name)
 | 
				
			||||||
    except docker.errors.NotFound as e:
 | 
					    except docker.errors.NotFound as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Get service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.NotFound": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        corpus.status = CorpusStatus.FAILED
 | 
					        corpus.status = CorpusStatus.FAILED
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Get service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    service_tasks = service.tasks()
 | 
					    service_tasks = service.tasks()
 | 
				
			||||||
    if not service_tasks:
 | 
					    if not service_tasks:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
@@ -129,11 +120,8 @@ def _checkout_build_corpus_service(corpus):
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service.remove()
 | 
					        service.remove()
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Remove service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Remove service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _create_cqpserver_container(corpus):
 | 
					def _create_cqpserver_container(corpus):
 | 
				
			||||||
    ''' # Docker container settings # '''
 | 
					    ''' # Docker container settings # '''
 | 
				
			||||||
@@ -174,20 +162,14 @@ def _create_cqpserver_container(corpus):
 | 
				
			|||||||
        container = docker_client.containers.get(name)
 | 
					        container = docker_client.containers.get(name)
 | 
				
			||||||
    except docker.errors.NotFound:
 | 
					    except docker.errors.NotFound:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get container "{name}" failed: {e}')
 | 
				
			||||||
            f'Get container "{name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            container.remove(force=True)
 | 
					            container.remove(force=True)
 | 
				
			||||||
        except docker.errors.APIError as e:
 | 
					        except docker.errors.DockerException as e:
 | 
				
			||||||
            current_app.logger.error(
 | 
					            current_app.logger.error(f'Remove container "{name}" failed: {e}')
 | 
				
			||||||
                f'Remove container "{name}" failed '
 | 
					 | 
				
			||||||
                f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        docker_client.containers.run(
 | 
					        docker_client.containers.run(
 | 
				
			||||||
@@ -207,11 +189,8 @@ def _create_cqpserver_container(corpus):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        corpus.status = CorpusStatus.FAILED
 | 
					        corpus.status = CorpusStatus.FAILED
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Run container "{name}" failed: {e}')
 | 
				
			||||||
            f'Run container "{name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError" error: {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    corpus.status = CorpusStatus.RUNNING_ANALYSIS_SESSION
 | 
					    corpus.status = CorpusStatus.RUNNING_ANALYSIS_SESSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -220,17 +199,11 @@ def _checkout_analysing_corpus_container(corpus):
 | 
				
			|||||||
    try:
 | 
					    try:
 | 
				
			||||||
        docker_client.containers.get(container_name)
 | 
					        docker_client.containers.get(container_name)
 | 
				
			||||||
    except docker.errors.NotFound as e:
 | 
					    except docker.errors.NotFound as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get container "{container_name}" failed: {e}')
 | 
				
			||||||
            f'Get container "{container_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.NotFound": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        corpus.num_analysis_sessions = 0
 | 
					        corpus.num_analysis_sessions = 0
 | 
				
			||||||
        corpus.status = CorpusStatus.BUILT
 | 
					        corpus.status = CorpusStatus.BUILT
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get container "{container_name}" failed: {e}')
 | 
				
			||||||
            f'Get container "{container_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _remove_cqpserver_container(corpus):
 | 
					def _remove_cqpserver_container(corpus):
 | 
				
			||||||
    container_name = f'cqpserver_{corpus.id}'
 | 
					    container_name = f'cqpserver_{corpus.id}'
 | 
				
			||||||
@@ -239,16 +212,10 @@ def _remove_cqpserver_container(corpus):
 | 
				
			|||||||
    except docker.errors.NotFound:
 | 
					    except docker.errors.NotFound:
 | 
				
			||||||
        corpus.status = CorpusStatus.BUILT
 | 
					        corpus.status = CorpusStatus.BUILT
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get container "{container_name}" failed: {e}')
 | 
				
			||||||
            f'Get container "{container_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        container.remove(force=True)
 | 
					        container.remove(force=True)
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Remove container "{container_name}" failed: {e}')
 | 
				
			||||||
            f'Remove container "{container_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,11 +134,8 @@ def _create_job_service(job):
 | 
				
			|||||||
            restart_policy=restart_policy,
 | 
					            restart_policy=restart_policy,
 | 
				
			||||||
            user='0:0'
 | 
					            user='0:0'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Create service "{name}" failed: {e}')
 | 
				
			||||||
            f'Create service "{name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    job.status = JobStatus.QUEUED
 | 
					    job.status = JobStatus.QUEUED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -147,17 +144,11 @@ def _checkout_job_service(job):
 | 
				
			|||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service = docker_client.services.get(service_name)
 | 
					        service = docker_client.services.get(service_name)
 | 
				
			||||||
    except docker.errors.NotFound as e:
 | 
					    except docker.errors.NotFound as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Get service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.NotFound": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        job.status = JobStatus.FAILED
 | 
					        job.status = JobStatus.FAILED
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Get service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    service_tasks = service.tasks()
 | 
					    service_tasks = service.tasks()
 | 
				
			||||||
    if not service_tasks:
 | 
					    if not service_tasks:
 | 
				
			||||||
@@ -194,11 +185,8 @@ def _checkout_job_service(job):
 | 
				
			|||||||
    job.end_date = datetime.utcnow()
 | 
					    job.end_date = datetime.utcnow()
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service.remove()
 | 
					        service.remove()
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Remove service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Remove service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _remove_job_service(job):
 | 
					def _remove_job_service(job):
 | 
				
			||||||
    service_name = f'job_{job.id}'
 | 
					    service_name = f'job_{job.id}'
 | 
				
			||||||
@@ -207,24 +195,15 @@ def _remove_job_service(job):
 | 
				
			|||||||
    except docker.errors.NotFound:
 | 
					    except docker.errors.NotFound:
 | 
				
			||||||
        job.status = JobStatus.CANCELED
 | 
					        job.status = JobStatus.CANCELED
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Get service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Get service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service.update(mounts=None)
 | 
					        service.update(mounts=None)
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Update service "{service_name}" failed: {e}')
 | 
				
			||||||
            f'Update service "{service_name}" failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        service.remove()
 | 
					        service.remove()
 | 
				
			||||||
    except docker.errors.APIError as e:
 | 
					    except docker.errors.DockerException as e:
 | 
				
			||||||
        current_app.logger.error(
 | 
					        current_app.logger.error(f'Remove "{service_name}" service failed: {e}')
 | 
				
			||||||
            f'Remove "{service_name}" service failed '
 | 
					 | 
				
			||||||
            f'due to "docker.errors.APIError": {e}'
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
from app.daemon import daemon
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def register(app, scheduler):
 | 
					 | 
				
			||||||
    if app.config['NOPAQUE_IS_PRIMARY_INSTANCE']:
 | 
					 | 
				
			||||||
        @scheduler.task('interval', id='daemon', seconds=3)
 | 
					 | 
				
			||||||
        def daemon_task():
 | 
					 | 
				
			||||||
            with app.app_context():
 | 
					 | 
				
			||||||
                daemon()
 | 
					 | 
				
			||||||
							
								
								
									
										13
									
								
								config.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								config.py
									
									
									
									
									
								
							@@ -18,6 +18,9 @@ class Config:
 | 
				
			|||||||
    SESSION_COOKIE_SECURE = \
 | 
					    SESSION_COOKIE_SECURE = \
 | 
				
			||||||
        os.environ.get('SESSION_COOKIE_SECURE', 'false').lower() == 'true'
 | 
					        os.environ.get('SESSION_COOKIE_SECURE', 'false').lower() == 'true'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ''' # Flask-APScheduler # '''
 | 
				
			||||||
 | 
					    JOBS = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ''' # Flask-Assets '''
 | 
					    ''' # Flask-Assets '''
 | 
				
			||||||
    ASSETS_DEBUG = os.environ.get('ASSETS_DEBUG', 'false').lower() == 'true'
 | 
					    ASSETS_DEBUG = os.environ.get('ASSETS_DEBUG', 'false').lower() == 'true'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -112,6 +115,16 @@ class Config:
 | 
				
			|||||||
            fmt=app.config['NOPAQUE_LOG_FORMAT'],
 | 
					            fmt=app.config['NOPAQUE_LOG_FORMAT'],
 | 
				
			||||||
            datefmt=app.config['NOPAQUE_LOG_DATE_FORMAT']
 | 
					            datefmt=app.config['NOPAQUE_LOG_DATE_FORMAT']
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        if app.config['NOPAQUE_IS_PRIMARY_INSTANCE']:
 | 
				
			||||||
 | 
					           app.config['JOBS'].append(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "id": "daemon",
 | 
				
			||||||
 | 
					                    "func": "app.daemon:daemon",
 | 
				
			||||||
 | 
					                    "args": (app,),
 | 
				
			||||||
 | 
					                    "trigger": "interval",
 | 
				
			||||||
 | 
					                    "seconds": 3,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					           )
 | 
				
			||||||
        if app.config['NOPAQUE_LOG_STDERR_ENABLED']:
 | 
					        if app.config['NOPAQUE_LOG_STDERR_ENABLED']:
 | 
				
			||||||
            stream_handler = logging.StreamHandler()
 | 
					            stream_handler = logging.StreamHandler()
 | 
				
			||||||
            stream_handler.setFormatter(formatter)
 | 
					            stream_handler.setFormatter(formatter)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user