mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #956 from YunoHost/be-more-robust-against-corrupted-backup-archives
Be more robust against some situation where archive is corrupted
This commit is contained in:
commit
7e4f3b6f29
3 changed files with 42 additions and 19 deletions
|
@ -74,6 +74,8 @@
|
||||||
"backup_archive_name_exists": "A backup archive with this name already exists.",
|
"backup_archive_name_exists": "A backup archive with this name already exists.",
|
||||||
"backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'",
|
"backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'",
|
||||||
"backup_archive_open_failed": "Could not open the backup archive",
|
"backup_archive_open_failed": "Could not open the backup archive",
|
||||||
|
"backup_archive_cant_retrieve_info_json": "Could not load infos for archive '{archive}' ... The info.json cannot be retrieved (or is not a valid json).",
|
||||||
|
"backup_archive_corrupted": "It looks like the backup archive '{archive}' is corrupted : {error}",
|
||||||
"backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup",
|
"backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup",
|
||||||
"backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'",
|
"backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'",
|
||||||
"backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)",
|
"backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)",
|
||||||
|
@ -91,7 +93,6 @@
|
||||||
"backup_delete_error": "Could not delete '{path:s}'",
|
"backup_delete_error": "Could not delete '{path:s}'",
|
||||||
"backup_deleted": "Backup deleted",
|
"backup_deleted": "Backup deleted",
|
||||||
"backup_hook_unknown": "The backup hook '{hook:s}' is unknown",
|
"backup_hook_unknown": "The backup hook '{hook:s}' is unknown",
|
||||||
"backup_invalid_archive": "This is not a backup archive",
|
|
||||||
"backup_method_borg_finished": "Backup into Borg finished",
|
"backup_method_borg_finished": "Backup into Borg finished",
|
||||||
"backup_method_copy_finished": "Backup copy finalized",
|
"backup_method_copy_finished": "Backup copy finalized",
|
||||||
"backup_method_custom_finished": "Custom backup method '{method:s}' finished",
|
"backup_method_custom_finished": "Custom backup method '{method:s}' finished",
|
||||||
|
|
|
@ -872,7 +872,7 @@ class RestoreManager():
|
||||||
Read the info file from inside an archive
|
Read the info file from inside an archive
|
||||||
|
|
||||||
Exceptions:
|
Exceptions:
|
||||||
backup_invalid_archive -- Raised if we can't read the info
|
backup_archive_cant_retrieve_info_json -- Raised if we can't read the info
|
||||||
"""
|
"""
|
||||||
# Retrieve backup info
|
# Retrieve backup info
|
||||||
info_file = os.path.join(self.work_dir, "info.json")
|
info_file = os.path.join(self.work_dir, "info.json")
|
||||||
|
@ -885,7 +885,7 @@ class RestoreManager():
|
||||||
self.info["system"] = self.info["hooks"]
|
self.info["system"] = self.info["hooks"]
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
||||||
raise YunohostError('backup_invalid_archive')
|
raise YunohostError('backup_archive_cant_retrieve_info_json', archive=self.archive_path)
|
||||||
else:
|
else:
|
||||||
logger.debug("restoring from backup '%s' created on %s", self.name,
|
logger.debug("restoring from backup '%s' created on %s", self.name,
|
||||||
datetime.utcfromtimestamp(self.info['created_at']))
|
datetime.utcfromtimestamp(self.info['created_at']))
|
||||||
|
@ -893,10 +893,6 @@ class RestoreManager():
|
||||||
def _postinstall_if_needed(self):
|
def _postinstall_if_needed(self):
|
||||||
"""
|
"""
|
||||||
Post install yunohost if needed
|
Post install yunohost if needed
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
backup_invalid_archive -- Raised if the current_host isn't in the
|
|
||||||
archive
|
|
||||||
"""
|
"""
|
||||||
# Check if YunoHost is installed
|
# Check if YunoHost is installed
|
||||||
if not os.path.isfile('/etc/yunohost/installed'):
|
if not os.path.isfile('/etc/yunohost/installed'):
|
||||||
|
@ -908,7 +904,7 @@ class RestoreManager():
|
||||||
logger.debug("unable to retrieve current_host from the backup",
|
logger.debug("unable to retrieve current_host from the backup",
|
||||||
exc_info=1)
|
exc_info=1)
|
||||||
# FIXME include the current_host by default ?
|
# FIXME include the current_host by default ?
|
||||||
raise YunohostError('backup_invalid_archive')
|
raise YunohostError("The main domain name cannot be retrieved from inside the archive, and is needed to perform the postinstall", raw_msg=True)
|
||||||
|
|
||||||
logger.debug("executing the post-install...")
|
logger.debug("executing the post-install...")
|
||||||
tools_postinstall(domain, 'Yunohost', True)
|
tools_postinstall(domain, 'Yunohost', True)
|
||||||
|
@ -1915,6 +1911,8 @@ class TarBackupMethod(BackupMethod):
|
||||||
|
|
||||||
Exceptions:
|
Exceptions:
|
||||||
backup_archive_open_failed -- Raised if the archive can't be open
|
backup_archive_open_failed -- Raised if the archive can't be open
|
||||||
|
backup_archive_corrupted -- Raised if the archive appears corrupted
|
||||||
|
backup_archive_cant_retrieve_info_json -- If the info.json file can't be retrieved
|
||||||
"""
|
"""
|
||||||
super(TarBackupMethod, self).mount(restore_manager)
|
super(TarBackupMethod, self).mount(restore_manager)
|
||||||
|
|
||||||
|
@ -1926,6 +1924,11 @@ class TarBackupMethod(BackupMethod):
|
||||||
self._archive_file, exc_info=1)
|
self._archive_file, exc_info=1)
|
||||||
raise YunohostError('backup_archive_open_failed')
|
raise YunohostError('backup_archive_open_failed')
|
||||||
|
|
||||||
|
try:
|
||||||
|
files_in_archive = tar.getnames()
|
||||||
|
except IOError as e:
|
||||||
|
raise YunohostError("backup_archive_corrupted", archive=self._archive_file, error=str(e))
|
||||||
|
|
||||||
# FIXME : Is this really useful to close the archive just to
|
# FIXME : Is this really useful to close the archive just to
|
||||||
# reopen it right after this with the same options ...?
|
# reopen it right after this with the same options ...?
|
||||||
tar.close()
|
tar.close()
|
||||||
|
@ -1934,21 +1937,21 @@ class TarBackupMethod(BackupMethod):
|
||||||
logger.debug(m18n.n("restore_extracting"))
|
logger.debug(m18n.n("restore_extracting"))
|
||||||
tar = tarfile.open(self._archive_file, "r:gz")
|
tar = tarfile.open(self._archive_file, "r:gz")
|
||||||
|
|
||||||
if "info.json" in tar.getnames():
|
if "info.json" in files_in_archive:
|
||||||
leading_dot = ""
|
leading_dot = ""
|
||||||
tar.extract('info.json', path=self.work_dir)
|
tar.extract('info.json', path=self.work_dir)
|
||||||
elif "./info.json" in tar.getnames():
|
elif "./info.json" in files_in_archive:
|
||||||
leading_dot = "./"
|
leading_dot = "./"
|
||||||
tar.extract('./info.json', path=self.work_dir)
|
tar.extract('./info.json', path=self.work_dir)
|
||||||
else:
|
else:
|
||||||
logger.debug("unable to retrieve 'info.json' inside the archive",
|
logger.debug("unable to retrieve 'info.json' inside the archive",
|
||||||
exc_info=1)
|
exc_info=1)
|
||||||
tar.close()
|
tar.close()
|
||||||
raise YunohostError('backup_invalid_archive')
|
raise YunohostError('backup_archive_cant_retrieve_info_json', archive=self._archive_file)
|
||||||
|
|
||||||
if "backup.csv" in tar.getnames():
|
if "backup.csv" in files_in_archive:
|
||||||
tar.extract('backup.csv', path=self.work_dir)
|
tar.extract('backup.csv', path=self.work_dir)
|
||||||
elif "./backup.csv" in tar.getnames():
|
elif "./backup.csv" in files_in_archive:
|
||||||
tar.extract('./backup.csv', path=self.work_dir)
|
tar.extract('./backup.csv', path=self.work_dir)
|
||||||
else:
|
else:
|
||||||
# Old backup archive have no backup.csv file
|
# Old backup archive have no backup.csv file
|
||||||
|
@ -2290,7 +2293,7 @@ def backup_list(with_info=False, human_readable=False):
|
||||||
try:
|
try:
|
||||||
d[a] = backup_info(a, human_readable=human_readable)
|
d[a] = backup_info(a, human_readable=human_readable)
|
||||||
except YunohostError as e:
|
except YunohostError as e:
|
||||||
logger.warning('%s: %s' % (a, e.strerror))
|
logger.warning(str(e))
|
||||||
|
|
||||||
result = d
|
result = d
|
||||||
|
|
||||||
|
@ -2327,17 +2330,23 @@ def backup_info(name, with_details=False, human_readable=False):
|
||||||
if not os.path.exists(info_file):
|
if not os.path.exists(info_file):
|
||||||
tar = tarfile.open(archive_file, "r:gz")
|
tar = tarfile.open(archive_file, "r:gz")
|
||||||
info_dir = info_file + '.d'
|
info_dir = info_file + '.d'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "info.json" in tar.getnames():
|
files_in_archive = tar.getnames()
|
||||||
|
except IOError as e:
|
||||||
|
raise YunohostError("backup_archive_corrupted", archive=archive_file, error=str(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if "info.json" in files_in_archive:
|
||||||
tar.extract('info.json', path=info_dir)
|
tar.extract('info.json', path=info_dir)
|
||||||
elif "./info.json" in tar.getnames():
|
elif "./info.json" in files_in_archive:
|
||||||
tar.extract('./info.json', path=info_dir)
|
tar.extract('./info.json', path=info_dir)
|
||||||
else:
|
else:
|
||||||
raise KeyError
|
raise KeyError
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.debug("unable to retrieve '%s' inside the archive",
|
logger.debug("unable to retrieve '%s' inside the archive",
|
||||||
info_file, exc_info=1)
|
info_file, exc_info=1)
|
||||||
raise YunohostError('backup_invalid_archive')
|
raise YunohostError('backup_archive_cant_retrieve_info_json', archive=archive_file)
|
||||||
else:
|
else:
|
||||||
shutil.move(os.path.join(info_dir, 'info.json'), info_file)
|
shutil.move(os.path.join(info_dir, 'info.json'), info_file)
|
||||||
finally:
|
finally:
|
||||||
|
@ -2350,7 +2359,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
||||||
info = json.load(f)
|
info = json.load(f)
|
||||||
except:
|
except:
|
||||||
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
logger.debug("unable to load '%s'", info_file, exc_info=1)
|
||||||
raise YunohostError('backup_invalid_archive')
|
raise YunohostError('backup_archive_cant_retrieve_info_json', archive=archive_file)
|
||||||
|
|
||||||
# Retrieve backup size
|
# Retrieve backup size
|
||||||
size = info.get('size', 0)
|
size = info.get('size', 0)
|
||||||
|
|
|
@ -574,9 +574,22 @@ def test_restore_archive_with_no_json(mocker):
|
||||||
|
|
||||||
assert "badbackup" in backup_list()["archives"]
|
assert "badbackup" in backup_list()["archives"]
|
||||||
|
|
||||||
with raiseYunohostError(mocker, 'backup_invalid_archive'):
|
with raiseYunohostError(mocker, 'backup_archive_cant_retrieve_info_json'):
|
||||||
backup_restore(name="badbackup", force=True)
|
backup_restore(name="badbackup", force=True)
|
||||||
|
|
||||||
|
@pytest.mark.with_wordpress_archive_from_2p4
|
||||||
|
def test_restore_archive_with_bad_archive(mocker):
|
||||||
|
|
||||||
|
# Break the archive
|
||||||
|
os.system("head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz > /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz")
|
||||||
|
|
||||||
|
assert "backup_wordpress_from_2p4" in backup_list()["archives"]
|
||||||
|
|
||||||
|
with raiseYunohostError(mocker, 'backup_archive_open_failed'):
|
||||||
|
backup_restore(name="backup_wordpress_from_2p4", force=True)
|
||||||
|
|
||||||
|
clean_tmp_backup_directory()
|
||||||
|
|
||||||
|
|
||||||
def test_backup_binds_are_readonly(mocker, monkeypatch):
|
def test_backup_binds_are_readonly(mocker, monkeypatch):
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue