From 09f7d7ac68f521359c928f92e0532951d7e148fc Mon Sep 17 00:00:00 2001 From: Stephan Porada <sporada@uni-bielefeld.de> Date: Tue, 17 Sep 2019 14:36:15 +0200 Subject: [PATCH] Add delete functions for jobs and users. --- app/auth/views.py | 34 +++++++++++++++--- app/main/views.py | 38 +++++++++++++-------- app/models.py | 17 +++++---- app/templates/admin/admin_user_page.html.j2 | 3 +- 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/app/auth/views.py b/app/auth/views.py index 7396e225..dd327cfb 100644 --- a/app/auth/views.py +++ b/app/auth/views.py @@ -1,10 +1,14 @@ -from flask import flash, redirect, render_template, request, url_for +from flask import (flash, redirect, render_template, request, url_for, + current_app) from flask_login import current_user, login_required, login_user, logout_user from . import auth from .. import db -from .forms import ChangePasswordForm, LoginForm, PasswordResetForm, PasswordResetRequestForm, RegistrationForm, EditProfileForm +from .forms import (ChangePasswordForm, LoginForm, PasswordResetForm, + PasswordResetRequestForm, RegistrationForm, EditProfileForm) from ..email import send_email from ..models import User, Job +import logging +import threading @auth.route('/login', methods=['GET', 'POST']) @@ -165,8 +169,30 @@ def settings(): @auth.route('/settings/delete_self', methods=['GET', 'POST']) @login_required def delete_self(): - user = current_user - db.session.delete(user) + logger = logging.getLogger(__name__) + + def background_delete(app, current_user_id): + with app.app_context(): + logger.warning('Called by delete_thread.') + logger.warning('User id is: {}.'.format(current_user_id)) + jobs = Job.query.join(User).filter_by(id=current_user_id).all() + logger.warning('Jobs to delete are: {}'.format(jobs)) + for job in jobs: + job.flag_for_stop() + logger.warning('Job status: {}'.format(job.status)) + deleted = False + while deleted is False: + db.session.refresh(job) + if job.status == 'deleted': + logger.warning('Job status is deleted.') + job.delete_job() + deleted = True + + delete_thread = threading.Thread(target=background_delete, + args=(current_app._get_current_object(), + current_user.id)) + delete_thread.start() + db.session.delete(current_user) db.session.commit() flash('Your account has been deleted!') return redirect(url_for('main.index')) diff --git a/app/main/views.py b/app/main/views.py index 5e81bbd8..51ba514e 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -7,7 +7,7 @@ from .. import db from ..models import Corpus, Job import os import logging -import time +import threading @main.route('/') @@ -145,17 +145,27 @@ def job_download(job_id): @login_required def delete_job(job_id): logger = logging.getLogger(__name__) - logger.warning(job_id) - job = Job.query.filter_by(id=job_id).first() - logger.warning('Job status: {}'.format(job.status)) - job.flag_for_stop() - logger.warning('Job status: {}'.format(job.status)) - deleted = False - while deleted is False: - db.session.refresh(job) - if job.status == 'deleted': - logger.warning('Job status is deleted.') - time.sleep(5) # Wait 5 seconds before deleteing job and job files - job.delete_job() # See delete_job() method for further explanation - deleted = True + + def background_delete(job_id, app): + with app.app_context(): + logger.warning('Called by delete_thread.') + logger.warning('Job id is: {}.'.format(job_id)) + job = Job.query.filter_by(id=job_id).first() + logger.warning('Job object is: {}'.format(job)) + logger.warning('Job status: {}'.format(job.status)) + job.flag_for_stop() + logger.warning('Job status: {}'.format(job.status)) + deleted = False + while deleted is False: + db.session.refresh(job) + if job.status == 'deleted': + logger.warning('Job status is deleted.') + job.delete_job() + deleted = True + + delete_thread = threading.Thread(target=background_delete, + args=(job_id, + current_app._get_current_object())) + delete_thread.start() + flash('Job has been deleted!') return redirect(url_for('main.dashboard')) diff --git a/app/models.py b/app/models.py index 494d0b03..3d4d4f0b 100644 --- a/app/models.py +++ b/app/models.py @@ -300,18 +300,23 @@ class Job(db.Model): def delete_job(self): """ Delete job with given job id from database. Also delete associated job - files. Wait 5 seconds after service has been flaged for stopping and - deleted. This method can only be used if the containers have been - totally stopped. Contianers are still running for a few seconds after - the associated service has been removed. + files. Contianers are still running for a few seconds after + the associated service has been removed. This is the reason for the + while loop. The loop checks if the file path to all the job files still + exists and removes it again and again till the container did shutdown + for good. See: https://docs.docker.com/engine/swarm/swarm-tutorial/delete-service/ """ logger = logging.getLogger(__name__) delete_path = os.path.join('/mnt/opaque/', str(self.user_id), 'jobs', str(self.id)) logger.warning('Delete path is: {}'.format(delete_path)) - if os.path.exists(delete_path): - shutil.rmtree(delete_path) + while os.path.exists(delete_path): + try: + shutil.rmtree(delete_path, ignore_errors=True) + logger.warning('Path does still exist.') + except OSError: + pass db.session.delete(self) db.session.commit() diff --git a/app/templates/admin/admin_user_page.html.j2 b/app/templates/admin/admin_user_page.html.j2 index ab4772f1..8a2204f7 100644 --- a/app/templates/admin/admin_user_page.html.j2 +++ b/app/templates/admin/admin_user_page.html.j2 @@ -76,7 +76,8 @@ <div id="modal-confirm-delete" class="modal"> <div class="modal-content"> <h4>Confirm deletion</h4> - <p>Do you really want to delete the current selected user ({{selected_user.username}})?</p> + <p>Do you really want to delete the current selected user ({{selected_user.username}})? + All associated jobs and job files will be permanently deleted.</p> </div> <div class="modal-footer"> <a href="{{url_for('admin.admin_delete_user', user_id=selected_user.id)}}" class="modal-close waves-effect waves-green btn red"><i class="material-icons left">delete</i>Delete User</a></a> -- GitLab