Skip to content

Commit 7ebd91a

Browse files
authored
force archive fixes (#11391)
1 parent f0f5ca9 commit 7ebd91a

File tree

3 files changed

+62
-19
lines changed

3 files changed

+62
-19
lines changed

admin/nodes/views.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from scripts.approve_registrations import approve_past_pendings
5454

5555
from website import settings, search
56+
from website.archiver.tasks import force_archive
5657

5758

5859
class NodeMixin(PermissionRequiredMixin):
@@ -832,16 +833,19 @@ class CheckArchiveStatusRegistrationsView(NodeMixin, View):
832833

833834
def get(self, request, *args, **kwargs):
834835
# Prevents circular imports that cause admin app to hang at startup
835-
from osf.management.commands.force_archive import check
836+
from osf.management.commands.force_archive import check, DEFAULT_PERMISSIBLE_ADDONS
836837

837838
registration = self.get_object()
838839

839840
if registration.archived:
840841
messages.success(request, f"Registration {registration._id} is archived.")
841842
return redirect(self.get_success_url())
842843

844+
addons = set(registration.registered_from.get_addon_names())
845+
addons.update(DEFAULT_PERMISSIBLE_ADDONS)
846+
843847
try:
844-
archive_status = check(registration)
848+
archive_status = check(registration, permissible_addons=addons)
845849
messages.success(request, archive_status)
846850
except RegistrationStuckError as exc:
847851
messages.error(request, str(exc))
@@ -862,7 +866,7 @@ class ForceArchiveRegistrationsView(NodeMixin, View):
862866

863867
def post(self, request, *args, **kwargs):
864868
# Prevents circular imports that cause admin app to hang at startup
865-
from osf.management.commands.force_archive import verify, archive, DEFAULT_PERMISSIBLE_ADDONS
869+
from osf.management.commands.force_archive import verify, DEFAULT_PERMISSIBLE_ADDONS
866870

867871
registration = self.get_object()
868872
force_archive_params = request.POST
@@ -887,18 +891,14 @@ def post(self, request, *args, **kwargs):
887891
if dry_mode:
888892
messages.success(request, f"Registration {registration._id} can be archived.")
889893
else:
890-
try:
891-
archive(
892-
registration,
893-
permissible_addons=addons,
894-
allow_unconfigured=allow_unconfigured,
895-
skip_collisions=skip_collision,
896-
delete_collisions=delete_collision,
897-
)
898-
messages.success(request, 'Registration archive process has finished.')
899-
except Exception as exc:
900-
messages.error(request, f'This registration cannot be archived due to {exc.__class__.__name__}: {str(exc)}. '
901-
f'If the problem persists get a developer to fix it.')
894+
force_archive_task = force_archive.delay(
895+
str(registration._id),
896+
permissible_addons=list(addons),
897+
allow_unconfigured=allow_unconfigured,
898+
skip_collisions=skip_collision,
899+
delete_collisions=delete_collision,
900+
)
901+
messages.success(request, f'Registration archive process has started. Task id: {force_archive_task.id}.')
902902

903903
return redirect(self.get_success_url())
904904

osf/management/commands/force_archive.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@
3636
from addons.osfstorage.models import OsfStorageFile, OsfStorageFolder, OsfStorageFileNode
3737
from framework import sentry
3838
from framework.exceptions import HTTPError
39+
from osf import features
3940
from osf.models import AbstractNode, Node, NodeLog, Registration, BaseFileNode
4041
from osf.models.files import TrashedFileNode
42+
from osf.utils.requests import get_current_request
4143
from osf.exceptions import RegistrationStuckRecoverableException, RegistrationStuckBrokenException
4244
from api.base.utils import waterbutler_api_url_for
45+
from api.waffle.utils import flag_is_active
4346
from scripts import utils as script_utils
4447
from website.archiver import ARCHIVER_SUCCESS
4548
from website.settings import ARCHIVE_TIMEOUT_TIMEDELTA, ARCHIVE_PROVIDER, COOKIE_NAME
@@ -149,9 +152,11 @@ def complete_archive_target(reg, addon_short_name):
149152

150153
def perform_wb_copy(reg, node_settings, delete_collisions=False, skip_collisions=False):
151154
src, dst, user = reg.archive_job.info()
152-
if dst.files.filter(name=node_settings.archive_folder_name.replace('/', '-')).exists():
155+
dst_storage = dst.get_addon('osfstorage')
156+
archive_name = node_settings.archive_folder_name.replace('/', '-')
157+
if dst_storage.get_root().children.filter(name=archive_name).exists():
153158
if not delete_collisions and not skip_collisions:
154-
raise Exception('Archive folder for {} already exists. Investigate manually and rerun with either --delete-collisions or --skip-collisions')
159+
raise Exception(f'Archive folder for {archive_name} already exists. Investigate manually and rerun with either --delete-collisions or --skip-collisions')
155160
if delete_collisions:
156161
archive_folder = dst.files.exclude(type='osf.trashedfolder').get(name=node_settings.archive_folder_name.replace('/', '-'))
157162
logger.info(f'Removing {archive_folder}')
@@ -393,12 +398,23 @@ def archive(registration, *args, permissible_addons=DEFAULT_PERMISSIBLE_ADDONS,
393398
logger.info(f'Preparing to archive {reg._id}')
394399
for short_name in permissible_addons:
395400
node_settings = reg.registered_from.get_addon(short_name)
401+
if not node_settings and short_name != 'osfstorage' and flag_is_active(get_current_request(), features.ENABLE_GV):
402+
# get_addon() returns None for addons when archive is running inside of
403+
# the celery task. In this case, try to get addon settings from the GV
404+
try:
405+
from website.archiver.tasks import get_addon_from_gv
406+
node_settings = get_addon_from_gv(reg.registered_from, short_name, reg.registered_from.creator)
407+
except Exception as e:
408+
logger.warning(f'Could not load {short_name} from GV: {e}')
409+
396410
if not hasattr(node_settings, '_get_file_tree'):
397411
# Excludes invalid or None-type
412+
logger.warning(f"Skipping {short_name} for {registration._id}.")
398413
continue
399414
if not node_settings.configured:
400415
if not allow_unconfigured:
401416
raise Exception(f'{reg._id}: {short_name} on {reg.registered_from._id} is not configured. If this is permissible, re-run with `--allow-unconfigured`.')
417+
logger.warning(f"{short_name} is not configured for {registration._id}.")
402418
continue
403419
if not reg.archive_job.get_target(short_name) or reg.archive_job.get_target(short_name).status == ARCHIVER_SUCCESS:
404420
continue
@@ -486,7 +502,7 @@ def verify_registrations(registration_ids, permissible_addons):
486502
else:
487503
SKIPPED.append(reg)
488504

489-
def check(reg):
505+
def check(reg, *args, **kwargs):
490506
"""Check registration status. Raise exception if registration stuck."""
491507
logger.info(f'Checking {reg._id}')
492508
if reg.is_deleted:
@@ -503,7 +519,7 @@ def check(reg):
503519
still_archiving = not archive_tree_finished
504520
if still_archiving and root_job.datetime_initiated < expired_if_before:
505521
logger.warning(f'Registration {reg._id} is stuck in archiving')
506-
if verify(reg):
522+
if verify(reg, *args, **kwargs):
507523
raise RegistrationStuckRecoverableException(f'Registration {reg._id} is stuck and verified recoverable')
508524
else:
509525
raise RegistrationStuckBrokenException(f'Registration {reg._id} is stuck and verified broken')

website/archiver/tasks.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from osf.models import (
3636
ArchiveJob,
3737
AbstractNode,
38+
Registration,
3839
DraftRegistration,
3940
)
4041
from osf import features
@@ -370,3 +371,29 @@ def archive_success(self, dst_pk, job_pk):
370371
dst.sanction.ask(dst.get_active_contributors_recursive(unique_users=True))
371372

372373
dst.update_search()
374+
375+
376+
@celery_app.task(bind=True)
377+
def force_archive(self, registration_id, permissible_addons, allow_unconfigured=False, skip_collisions=False, delete_collisions=False):
378+
from osf.management.commands.force_archive import archive
379+
380+
create_app_context()
381+
382+
try:
383+
registration = AbstractNode.load(registration_id)
384+
if not registration or not isinstance(registration, Registration):
385+
return f'Registration {registration_id} not found'
386+
387+
archive(
388+
registration,
389+
permissible_addons=set(permissible_addons),
390+
allow_unconfigured=allow_unconfigured,
391+
skip_collisions=skip_collisions,
392+
delete_collisions=delete_collisions,
393+
)
394+
return f'Registration {registration_id} archive completed'
395+
396+
except Exception as exc:
397+
sentry.log_message(f'Archive task failed for {registration_id}: {exc}')
398+
sentry.log_exception(exc)
399+
return f'{exc.__class__.__name__}: {str(exc)}'

0 commit comments

Comments
 (0)