mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-11-20 08:20:15 +00:00
Remove customized unknown error types
This commit is contained in:
@@ -66,30 +66,16 @@ from ..docker.const import ContainerState
|
||||
from ..docker.monitor import DockerContainerStateEvent
|
||||
from ..docker.stats import DockerStats
|
||||
from ..exceptions import (
|
||||
AddonBackupAppArmorProfileUnknownError,
|
||||
AddonBackupExportImageUnknownError,
|
||||
AddonBackupMetadataInvalidError,
|
||||
AddonBuildImageUnknownError,
|
||||
AddonConfigurationFileUnknownError,
|
||||
AddonConfigurationInvalidError,
|
||||
AddonContainerRunCommandUnknownError,
|
||||
AddonContainerStartUnknownError,
|
||||
AddonContainerStatsUnknownError,
|
||||
AddonContainerStopUnknownError,
|
||||
AddonContainerWriteStdinUnknownError,
|
||||
AddonCreateBackupFileUnknownError,
|
||||
AddonCreateBackupMetadataFileUnknownError,
|
||||
AddonExtractBackupFileUnknownError,
|
||||
AddonInstallImageUnknownError,
|
||||
AddonNotRunningError,
|
||||
AddonNotSupportedError,
|
||||
AddonNotSupportedWriteStdinError,
|
||||
AddonPrePostBackupCommandReturnedError,
|
||||
AddonRemoveImageUnknownError,
|
||||
AddonRestoreAppArmorProfileUnknownError,
|
||||
AddonRestoreBackupDataUnknownError,
|
||||
AddonsError,
|
||||
AddonsJobError,
|
||||
AddonUnknownError,
|
||||
BackupRestoreUnknownError,
|
||||
ConfigurationFileError,
|
||||
DockerBuildError,
|
||||
DockerError,
|
||||
@@ -747,7 +733,7 @@ class Addon(AddonModel):
|
||||
) from None
|
||||
except ConfigurationFileError as err:
|
||||
_LOGGER.error("Add-on %s can't write options", self.slug)
|
||||
raise AddonConfigurationFileUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
_LOGGER.debug("Add-on %s write options: %s", self.slug, options)
|
||||
|
||||
@@ -817,11 +803,13 @@ class Addon(AddonModel):
|
||||
await self.sys_addons.data.uninstall(self)
|
||||
raise
|
||||
except DockerBuildError as err:
|
||||
_LOGGER.error("Could not build image for addon %s: %s", self.slug, err)
|
||||
await self.sys_addons.data.uninstall(self)
|
||||
raise AddonBuildImageUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
except DockerError as err:
|
||||
_LOGGER.error("Could not pull image to update addon %s: %s", self.slug, err)
|
||||
await self.sys_addons.data.uninstall(self)
|
||||
raise AddonInstallImageUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
# Finish initialization and set up listeners
|
||||
await self.load()
|
||||
@@ -845,7 +833,8 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.instance.remove(remove_image=remove_image)
|
||||
except DockerError as err:
|
||||
raise AddonRemoveImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error("Could not remove image for addon %s: %s", self.slug, err)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
self.state = AddonState.UNKNOWN
|
||||
|
||||
@@ -919,9 +908,11 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.instance.update(store.version, store.image, arch=self.arch)
|
||||
except DockerBuildError as err:
|
||||
raise AddonBuildImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error("Could not build image for addon %s: %s", self.slug, err)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
except DockerError as err:
|
||||
raise AddonInstallImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error("Could not pull image to update addon %s: %s", self.slug, err)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
# Stop the addon if running
|
||||
if (last_state := self.state) in {AddonState.STARTED, AddonState.STARTUP}:
|
||||
@@ -967,14 +958,19 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.instance.remove()
|
||||
except DockerError as err:
|
||||
raise AddonRemoveImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error("Could not remove image for addon %s: %s", self.slug, err)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
try:
|
||||
await self.instance.install(self.version)
|
||||
except DockerBuildError as err:
|
||||
raise AddonBuildImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error("Could not build image for addon %s: %s", self.slug, err)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
except DockerError as err:
|
||||
raise AddonInstallImageUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error(
|
||||
"Could not pull image to update addon %s: %s", self.slug, err
|
||||
)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
if self.addon_store:
|
||||
await self.sys_addons.data.update(self.addon_store)
|
||||
@@ -1145,8 +1141,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.instance.run()
|
||||
except DockerError as err:
|
||||
_LOGGER.error("Could not start container for addon %s: %s", self.slug, err)
|
||||
self.state = AddonState.ERROR
|
||||
raise AddonContainerStartUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
return self.sys_create_task(self._wait_for_startup())
|
||||
|
||||
@@ -1161,8 +1158,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.instance.stop()
|
||||
except DockerError as err:
|
||||
_LOGGER.error("Could not stop container for addon %s: %s", self.slug, err)
|
||||
self.state = AddonState.ERROR
|
||||
raise AddonContainerStopUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
@Job(
|
||||
name="addon_restart",
|
||||
@@ -1200,7 +1198,10 @@ class Addon(AddonModel):
|
||||
|
||||
return await self.instance.stats()
|
||||
except DockerError as err:
|
||||
raise AddonContainerStatsUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error(
|
||||
"Could not get stats of container for addon %s: %s", self.slug, err
|
||||
)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
@Job(
|
||||
name="addon_write_stdin",
|
||||
@@ -1218,7 +1219,10 @@ class Addon(AddonModel):
|
||||
|
||||
await self.instance.write_stdin(data)
|
||||
except DockerError as err:
|
||||
raise AddonContainerWriteStdinUnknownError(addon=self.slug) from err
|
||||
_LOGGER.error(
|
||||
"Could not write stdin to container for addon %s: %s", self.slug, err
|
||||
)
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
async def _backup_command(self, command: str) -> None:
|
||||
try:
|
||||
@@ -1234,7 +1238,7 @@ class Addon(AddonModel):
|
||||
_LOGGER.error(
|
||||
"Failed running pre-/post backup command %s: %s", command, err
|
||||
)
|
||||
raise AddonContainerRunCommandUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
@Job(
|
||||
name="addon_begin_backup",
|
||||
@@ -1323,18 +1327,14 @@ class Addon(AddonModel):
|
||||
try:
|
||||
self.instance.export_image(temp_path.joinpath("image.tar"))
|
||||
except DockerError as err:
|
||||
raise AddonBackupExportImageUnknownError(
|
||||
addon=self.slug
|
||||
) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
|
||||
# Store local configs/state
|
||||
try:
|
||||
write_json_file(temp_path.joinpath("addon.json"), metadata)
|
||||
except ConfigurationFileError as err:
|
||||
_LOGGER.error("Can't save meta for %s: %s", self.slug, err)
|
||||
raise AddonCreateBackupMetadataFileUnknownError(
|
||||
addon=self.slug
|
||||
) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
|
||||
# Store AppArmor Profile
|
||||
if apparmor_profile:
|
||||
@@ -1344,9 +1344,7 @@ class Addon(AddonModel):
|
||||
apparmor_profile, profile_backup_file
|
||||
)
|
||||
except HostAppArmorError as err:
|
||||
raise AddonBackupAppArmorProfileUnknownError(
|
||||
addon=self.slug
|
||||
) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
|
||||
# Write tarfile
|
||||
with tar_file as backup:
|
||||
@@ -1401,7 +1399,7 @@ class Addon(AddonModel):
|
||||
_LOGGER.info("Finish backup for addon %s", self.slug)
|
||||
except (tarfile.TarError, OSError, AddFileError) as err:
|
||||
_LOGGER.error("Can't write backup tarfile for addon %s: %s", self.slug, err)
|
||||
raise AddonCreateBackupFileUnknownError(addon=self.slug) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
finally:
|
||||
if was_running:
|
||||
wait_for_start = await self.end_backup()
|
||||
@@ -1444,9 +1442,9 @@ class Addon(AddonModel):
|
||||
tmp, data = await self.sys_run_in_executor(_extract_tarfile)
|
||||
except tarfile.TarError as err:
|
||||
_LOGGER.error("Can't extract backup tarfile for %s: %s", self.slug, err)
|
||||
raise AddonExtractBackupFileUnknownError(addon=self.slug) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
except ConfigurationFileError as err:
|
||||
raise AddonConfigurationFileUnknownError(addon=self.slug) from err
|
||||
raise AddonUnknownError(addon=self.slug) from err
|
||||
|
||||
try:
|
||||
# Validate
|
||||
@@ -1522,7 +1520,7 @@ class Addon(AddonModel):
|
||||
_LOGGER.error(
|
||||
"Can't restore origin data for %s: %s", self.slug, err
|
||||
)
|
||||
raise AddonRestoreBackupDataUnknownError(addon=self.slug) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
|
||||
# Restore AppArmor
|
||||
profile_file = Path(tmp.name, "apparmor.txt")
|
||||
@@ -1537,9 +1535,7 @@ class Addon(AddonModel):
|
||||
self.slug,
|
||||
err,
|
||||
)
|
||||
raise AddonRestoreAppArmorProfileUnknownError(
|
||||
addon=self.slug
|
||||
) from err
|
||||
raise BackupRestoreUnknownError() from err
|
||||
|
||||
finally:
|
||||
# Is add-on loaded
|
||||
|
||||
@@ -628,9 +628,6 @@ class Backup(JobGroup):
|
||||
if start_task := await self._addon_save(addon):
|
||||
start_tasks.append(start_task)
|
||||
except BackupError as err:
|
||||
err = BackupError(
|
||||
f"Can't backup add-on {addon.slug}: {str(err)}", _LOGGER.error
|
||||
)
|
||||
self.sys_jobs.current.capture_error(err)
|
||||
|
||||
return start_tasks
|
||||
|
||||
@@ -358,26 +358,6 @@ class AddonConfigurationInvalidError(AddonConfigurationError, APIError):
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class AddonBackupMetadataInvalidError(AddonsError, APIError):
|
||||
"""Raise if invalid metadata file provided for addon in backup."""
|
||||
|
||||
error_key = "addon_backup_metadata_invalid_error"
|
||||
message_template = (
|
||||
"Metadata file for add-on {addon} in backup is invalid: {validation_error}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
addon: str,
|
||||
validation_error: str,
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon, "validation_error": validation_error}
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class AddonBootConfigCannotChangeError(AddonsError, APIError):
|
||||
"""Raise if user attempts to change addon boot config when it can't be changed."""
|
||||
|
||||
@@ -408,28 +388,6 @@ class AddonNotRunningError(AddonsError, APIError):
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class AddonPrePostBackupCommandReturnedError(AddonsError, APIError):
|
||||
"""Raise when addon's pre/post backup command returns an error."""
|
||||
|
||||
error_key = "addon_pre_post_backup_command_returned_error"
|
||||
message_template = (
|
||||
"Pre-/Post backup command for add-on {addon} returned error code: "
|
||||
"{exit_code}. Please report this to the addon developer. Enable debug "
|
||||
"logging to capture complete command output using {debug_logging_command}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str, exit_code: int
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {
|
||||
"addon": addon,
|
||||
"exit_code": exit_code,
|
||||
"debug_logging_command": "ha supervisor options --logging debug",
|
||||
}
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class AddonNotSupportedError(HassioNotSupportedError):
|
||||
"""Addon doesn't support a function."""
|
||||
|
||||
@@ -546,238 +504,11 @@ class AddonBuildArchitectureNotSupportedError(AddonNotSupportedError, APIError):
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
# pylint: disable-next=too-many-ancestors
|
||||
class AddonConfigurationFileUnknownError(
|
||||
AddonConfigurationError, APIUnknownSupervisorError
|
||||
):
|
||||
"""Raise when unknown error occurs trying to read/write addon configuration file."""
|
||||
class AddonUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when unknown error occurs taking an action for an addon."""
|
||||
|
||||
error_key = "addon_configuration_file_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred reading/writing configuration file for {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonBuildImageUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs during image build."""
|
||||
|
||||
error_key = "addon_build_image_unknown_error"
|
||||
message_template = "An unknown error occurred during build of image for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonInstallImageUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs during image install."""
|
||||
|
||||
error_key = "addon_install_image_unknown_error"
|
||||
message_template = "An unknown error occurred during install of image for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonRemoveImageUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while removing an image."""
|
||||
|
||||
error_key = "addon_remove_image_unknown_error"
|
||||
message_template = "An unknown error occurred while removing image for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonContainerStartUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while starting a container."""
|
||||
|
||||
error_key = "addon_container_start_unknown_error"
|
||||
message_template = "An unknown error occurred while starting container for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonContainerStopUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while stopping a container."""
|
||||
|
||||
error_key = "addon_container_stop_unknown_error"
|
||||
message_template = "An unknown error occurred while stopping container for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonContainerStatsUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while getting stats of a container."""
|
||||
|
||||
error_key = "addon_container_stats_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while getting stats of container for {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonContainerWriteStdinUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while writing to stdin of a container."""
|
||||
|
||||
error_key = "addon_container_write_stdin_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while writing to stdin of container for {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonContainerRunCommandUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while running command inside of a container."""
|
||||
|
||||
error_key = "addon_container_run_command_unknown_error"
|
||||
message_template = "An unknown error occurred while running a command inside of container for {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonCreateBackupFileUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while making the backup file for an addon."""
|
||||
|
||||
error_key = "addon_create_backup_file_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while creating the backup file for {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonCreateBackupMetadataFileUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while making the metadata file for an addon backup."""
|
||||
|
||||
error_key = "addon_create_backup_metadata_file_unknown_error"
|
||||
message_template = "An unknown error occurred while creating the metadata file for backup of {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonBackupAppArmorProfileUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while backing up the AppArmor profile of an addon."""
|
||||
|
||||
error_key = "addon_backup_app_armor_profile_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while backing up the AppArmor profile of {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonBackupExportImageUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while exporting image for an addon backup."""
|
||||
|
||||
error_key = "addon_backup_export_image_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while exporting image to back up {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonExtractBackupFileUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs while extracting backup file for an addon."""
|
||||
|
||||
error_key = "addon_extract_backup_file_unknown_error"
|
||||
message_template = (
|
||||
"An unknown error occurred while extracting the backup file for {addon}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonRestoreBackupDataUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when unknown error occurs while restoring data/config for addon from backup."""
|
||||
|
||||
error_key = "addon_restore_backup_data_unknown_error"
|
||||
message_template = "An unknown error occurred while restoring data and config for {addon} from backup"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon}
|
||||
super().__init__(logger)
|
||||
|
||||
|
||||
class AddonRestoreAppArmorProfileUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||
"""Raise when unknown error occurs while restoring AppArmor profile for addon from backup."""
|
||||
|
||||
error_key = "addon_restore_app_armor_profile_unknown_error"
|
||||
message_template = "An unknown error occurred while restoring AppArmor profile for {addon} from backup"
|
||||
error_key = "addon_unknown_error"
|
||||
message_template = "An unknown error occurred with addon {addon}"
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||
@@ -1262,6 +993,55 @@ class BackupFileExistError(BackupError):
|
||||
"""Raise if the backup file already exists."""
|
||||
|
||||
|
||||
class AddonBackupMetadataInvalidError(BackupError, APIError):
|
||||
"""Raise if invalid metadata file provided for addon in backup."""
|
||||
|
||||
error_key = "addon_backup_metadata_invalid_error"
|
||||
message_template = (
|
||||
"Metadata file for add-on {addon} in backup is invalid: {validation_error}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Callable[..., None] | None = None,
|
||||
*,
|
||||
addon: str,
|
||||
validation_error: str,
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {"addon": addon, "validation_error": validation_error}
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class AddonPrePostBackupCommandReturnedError(BackupError, APIError):
|
||||
"""Raise when addon's pre/post backup command returns an error."""
|
||||
|
||||
error_key = "addon_pre_post_backup_command_returned_error"
|
||||
message_template = (
|
||||
"Pre-/Post backup command for add-on {addon} returned error code: "
|
||||
"{exit_code}. Please report this to the addon developer. Enable debug "
|
||||
"logging to capture complete command output using {debug_logging_command}"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, logger: Callable[..., None] | None = None, *, addon: str, exit_code: int
|
||||
) -> None:
|
||||
"""Initialize exception."""
|
||||
self.extra_fields = {
|
||||
"addon": addon,
|
||||
"exit_code": exit_code,
|
||||
"debug_logging_command": "ha supervisor options --logging debug",
|
||||
}
|
||||
super().__init__(None, logger)
|
||||
|
||||
|
||||
class BackupRestoreUnknownError(BackupError, APIUnknownSupervisorError):
|
||||
"""Raise when an unknown error occurs during backup or restore."""
|
||||
|
||||
error_key = "backup_restore_unknown_error"
|
||||
message_template = "An unknown error occurred during backup/restore"
|
||||
|
||||
|
||||
# Security
|
||||
|
||||
|
||||
|
||||
@@ -102,13 +102,17 @@ class SupervisorJobError:
|
||||
"Unknown error, see Supervisor logs (check with 'ha supervisor logs')"
|
||||
)
|
||||
stage: str | None = None
|
||||
error_key: str | None = None
|
||||
extra_fields: dict[str, Any] | None = None
|
||||
|
||||
def as_dict(self) -> dict[str, str | None]:
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
"""Return dictionary representation."""
|
||||
return {
|
||||
"type": self.type_.__name__,
|
||||
"message": self.message,
|
||||
"stage": self.stage,
|
||||
"error_key": self.error_key,
|
||||
"extra_fields": self.extra_fields,
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +162,9 @@ class SupervisorJob:
|
||||
def capture_error(self, err: HassioError | None = None) -> None:
|
||||
"""Capture an error or record that an unknown error has occurred."""
|
||||
if err:
|
||||
new_error = SupervisorJobError(type(err), str(err), self.stage)
|
||||
new_error = SupervisorJobError(
|
||||
type(err), str(err), self.stage, err.error_key, err.extra_fields
|
||||
)
|
||||
else:
|
||||
new_error = SupervisorJobError(stage=self.stage)
|
||||
self.errors += [new_error]
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import timedelta
|
||||
import errno
|
||||
from http import HTTPStatus
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, PropertyMock, call, patch
|
||||
|
||||
import aiodocker
|
||||
@@ -23,7 +24,13 @@ from supervisor.docker.addon import DockerAddon
|
||||
from supervisor.docker.const import ContainerState
|
||||
from supervisor.docker.manager import CommandReturn, DockerAPI
|
||||
from supervisor.docker.monitor import DockerContainerStateEvent
|
||||
from supervisor.exceptions import AddonsError, AddonsJobError, AudioUpdateError
|
||||
from supervisor.exceptions import (
|
||||
AddonPrePostBackupCommandReturnedError,
|
||||
AddonsJobError,
|
||||
AddonUnknownError,
|
||||
AudioUpdateError,
|
||||
HassioError,
|
||||
)
|
||||
from supervisor.hardware.helper import HwHelper
|
||||
from supervisor.ingress import Ingress
|
||||
from supervisor.store.repository import Repository
|
||||
@@ -502,31 +509,26 @@ async def test_backup_with_pre_post_command(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"get_error,exception_on_exec",
|
||||
("container_get_side_effect", "exec_run_side_effect", "exc_type_raised"),
|
||||
[
|
||||
(NotFound("missing"), False),
|
||||
(DockerException(), False),
|
||||
(None, True),
|
||||
(None, False),
|
||||
(NotFound("missing"), [(1, None)], AddonUnknownError),
|
||||
(DockerException(), [(1, None)], AddonUnknownError),
|
||||
(None, DockerException(), AddonUnknownError),
|
||||
(None, [(1, None)], AddonPrePostBackupCommandReturnedError),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
||||
async def test_backup_with_pre_command_error(
|
||||
coresys: CoreSys,
|
||||
install_addon_ssh: Addon,
|
||||
container: MagicMock,
|
||||
get_error: DockerException | None,
|
||||
exception_on_exec: bool,
|
||||
tmp_supervisor_data,
|
||||
path_extern,
|
||||
container_get_side_effect: DockerException | None,
|
||||
exec_run_side_effect: DockerException | list[tuple[int, Any]],
|
||||
exc_type_raised: type[HassioError],
|
||||
) -> None:
|
||||
"""Test backing up an addon with error running pre command."""
|
||||
if get_error:
|
||||
coresys.docker.containers.get.side_effect = get_error
|
||||
|
||||
if exception_on_exec:
|
||||
container.exec_run.side_effect = DockerException()
|
||||
else:
|
||||
container.exec_run.return_value = (1, None)
|
||||
coresys.docker.containers.get.side_effect = container_get_side_effect
|
||||
container.exec_run.side_effect = exec_run_side_effect
|
||||
|
||||
install_addon_ssh.path_data.mkdir()
|
||||
await install_addon_ssh.load()
|
||||
@@ -535,7 +537,7 @@ async def test_backup_with_pre_command_error(
|
||||
with (
|
||||
patch.object(DockerAddon, "is_running", return_value=True),
|
||||
patch.object(Addon, "backup_pre", new=PropertyMock(return_value="backup_pre")),
|
||||
pytest.raises(AddonsError),
|
||||
pytest.raises(exc_type_raised),
|
||||
):
|
||||
assert await install_addon_ssh.backup(tarfile) is None
|
||||
|
||||
|
||||
@@ -590,9 +590,9 @@ async def test_addon_start_options_error(
|
||||
body = await resp.json()
|
||||
assert (
|
||||
body["message"]
|
||||
== "An unknown error occurred reading/writing configuration file for local_example. Check supervisor logs for details (check with 'ha supervisor logs')"
|
||||
== "An unknown error occurred with addon local_example. Check supervisor logs for details (check with 'ha supervisor logs')"
|
||||
)
|
||||
assert body["error_key"] == "addon_configuration_file_unknown_error"
|
||||
assert body["error_key"] == "addon_unknown_error"
|
||||
assert body["extra_fields"] == {
|
||||
"addon": "local_example",
|
||||
"logs_command": "ha supervisor logs",
|
||||
|
||||
@@ -17,6 +17,7 @@ from supervisor.const import CoreState
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.docker.manager import DockerAPI
|
||||
from supervisor.exceptions import (
|
||||
AddonPrePostBackupCommandReturnedError,
|
||||
AddonsError,
|
||||
BackupInvalidError,
|
||||
HomeAssistantBackupError,
|
||||
@@ -24,6 +25,7 @@ from supervisor.exceptions import (
|
||||
from supervisor.homeassistant.core import HomeAssistantCore
|
||||
from supervisor.homeassistant.module import HomeAssistant
|
||||
from supervisor.homeassistant.websocket import HomeAssistantWebSocket
|
||||
from supervisor.jobs import SupervisorJob
|
||||
from supervisor.mounts.mount import Mount
|
||||
from supervisor.supervisor import Supervisor
|
||||
|
||||
@@ -401,6 +403,8 @@ async def test_api_backup_errors(
|
||||
"type": "BackupError",
|
||||
"message": str(err),
|
||||
"stage": None,
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
]
|
||||
assert job["child_jobs"][2]["name"] == "backup_store_folders"
|
||||
@@ -437,6 +441,8 @@ async def test_api_backup_errors(
|
||||
"type": "HomeAssistantBackupError",
|
||||
"message": "Backup error",
|
||||
"stage": "home_assistant",
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
]
|
||||
assert job["child_jobs"][0]["name"] == "backup_store_homeassistant"
|
||||
@@ -445,6 +451,8 @@ async def test_api_backup_errors(
|
||||
"type": "HomeAssistantBackupError",
|
||||
"message": "Backup error",
|
||||
"stage": None,
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
]
|
||||
assert len(job["child_jobs"]) == 1
|
||||
@@ -749,6 +757,8 @@ async def test_backup_to_multiple_locations_error_on_copy(
|
||||
"type": "BackupError",
|
||||
"message": "Could not copy backup to .cloud_backup due to: ",
|
||||
"stage": None,
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1483,3 +1493,44 @@ async def test_immediate_list_after_missing_file_restore(
|
||||
result = await resp.json()
|
||||
assert len(result["data"]["backups"]) == 1
|
||||
assert result["data"]["backups"][0]["slug"] == "93b462f8"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("command", ["backup_pre", "backup_post"])
|
||||
@pytest.mark.usefixtures("install_addon_example", "tmp_supervisor_data")
|
||||
async def test_pre_post_backup_command_error(
|
||||
api_client: TestClient, coresys: CoreSys, container: MagicMock, command: str
|
||||
):
|
||||
"""Test pre/post backup command error."""
|
||||
await coresys.core.set_state(CoreState.RUNNING)
|
||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||
|
||||
container.status = "running"
|
||||
container.exec_run.return_value = (1, b"")
|
||||
with patch.object(Addon, command, return_value=PropertyMock(return_value="test")):
|
||||
resp = await api_client.post(
|
||||
"/backups/new/partial", json={"addons": ["local_example"]}
|
||||
)
|
||||
|
||||
assert resp.status == 200
|
||||
body = await resp.json()
|
||||
job_id = body["data"]["job_id"]
|
||||
job: SupervisorJob | None = None
|
||||
for j in coresys.jobs.jobs:
|
||||
if j.name == "backup_store_addons" and j.parent_id == job_id:
|
||||
job = j
|
||||
break
|
||||
|
||||
assert job
|
||||
assert job.done is True
|
||||
assert job.errors[0].type_ == AddonPrePostBackupCommandReturnedError
|
||||
assert job.errors[0].message == (
|
||||
"Pre-/Post backup command for add-on local_example returned error code: "
|
||||
"1. Please report this to the addon developer. Enable debug "
|
||||
"logging to capture complete command output using ha supervisor options --logging debug"
|
||||
)
|
||||
assert job.errors[0].error_key == "addon_pre_post_backup_command_returned_error"
|
||||
assert job.errors[0].extra_fields == {
|
||||
"addon": "local_example",
|
||||
"exit_code": 1,
|
||||
"debug_logging_command": "ha supervisor options --logging debug",
|
||||
}
|
||||
|
||||
@@ -374,6 +374,8 @@ async def test_job_with_error(
|
||||
"type": "SupervisorError",
|
||||
"message": "bad",
|
||||
"stage": "test",
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
],
|
||||
"child_jobs": [
|
||||
@@ -391,6 +393,8 @@ async def test_job_with_error(
|
||||
"type": "SupervisorError",
|
||||
"message": "bad",
|
||||
"stage": None,
|
||||
"error_key": None,
|
||||
"extra_fields": None,
|
||||
}
|
||||
],
|
||||
"child_jobs": [],
|
||||
|
||||
Reference in New Issue
Block a user