mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-11-21 16:57:00 +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.monitor import DockerContainerStateEvent
|
||||||
from ..docker.stats import DockerStats
|
from ..docker.stats import DockerStats
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
AddonBackupAppArmorProfileUnknownError,
|
|
||||||
AddonBackupExportImageUnknownError,
|
|
||||||
AddonBackupMetadataInvalidError,
|
AddonBackupMetadataInvalidError,
|
||||||
AddonBuildImageUnknownError,
|
|
||||||
AddonConfigurationFileUnknownError,
|
|
||||||
AddonConfigurationInvalidError,
|
AddonConfigurationInvalidError,
|
||||||
AddonContainerRunCommandUnknownError,
|
|
||||||
AddonContainerStartUnknownError,
|
|
||||||
AddonContainerStatsUnknownError,
|
|
||||||
AddonContainerStopUnknownError,
|
|
||||||
AddonContainerWriteStdinUnknownError,
|
|
||||||
AddonCreateBackupFileUnknownError,
|
|
||||||
AddonCreateBackupMetadataFileUnknownError,
|
|
||||||
AddonExtractBackupFileUnknownError,
|
|
||||||
AddonInstallImageUnknownError,
|
|
||||||
AddonNotRunningError,
|
AddonNotRunningError,
|
||||||
AddonNotSupportedError,
|
AddonNotSupportedError,
|
||||||
AddonNotSupportedWriteStdinError,
|
AddonNotSupportedWriteStdinError,
|
||||||
AddonPrePostBackupCommandReturnedError,
|
AddonPrePostBackupCommandReturnedError,
|
||||||
AddonRemoveImageUnknownError,
|
|
||||||
AddonRestoreAppArmorProfileUnknownError,
|
|
||||||
AddonRestoreBackupDataUnknownError,
|
|
||||||
AddonsError,
|
AddonsError,
|
||||||
AddonsJobError,
|
AddonsJobError,
|
||||||
|
AddonUnknownError,
|
||||||
|
BackupRestoreUnknownError,
|
||||||
ConfigurationFileError,
|
ConfigurationFileError,
|
||||||
DockerBuildError,
|
DockerBuildError,
|
||||||
DockerError,
|
DockerError,
|
||||||
@@ -747,7 +733,7 @@ class Addon(AddonModel):
|
|||||||
) from None
|
) from None
|
||||||
except ConfigurationFileError as err:
|
except ConfigurationFileError as err:
|
||||||
_LOGGER.error("Add-on %s can't write options", self.slug)
|
_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)
|
_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)
|
await self.sys_addons.data.uninstall(self)
|
||||||
raise
|
raise
|
||||||
except DockerBuildError as err:
|
except DockerBuildError as err:
|
||||||
|
_LOGGER.error("Could not build image for addon %s: %s", self.slug, err)
|
||||||
await self.sys_addons.data.uninstall(self)
|
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:
|
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)
|
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
|
# Finish initialization and set up listeners
|
||||||
await self.load()
|
await self.load()
|
||||||
@@ -845,7 +833,8 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
await self.instance.remove(remove_image=remove_image)
|
await self.instance.remove(remove_image=remove_image)
|
||||||
except DockerError as err:
|
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
|
self.state = AddonState.UNKNOWN
|
||||||
|
|
||||||
@@ -919,9 +908,11 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
await self.instance.update(store.version, store.image, arch=self.arch)
|
await self.instance.update(store.version, store.image, arch=self.arch)
|
||||||
except DockerBuildError as err:
|
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:
|
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
|
# Stop the addon if running
|
||||||
if (last_state := self.state) in {AddonState.STARTED, AddonState.STARTUP}:
|
if (last_state := self.state) in {AddonState.STARTED, AddonState.STARTUP}:
|
||||||
@@ -967,14 +958,19 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
await self.instance.remove()
|
await self.instance.remove()
|
||||||
except DockerError as err:
|
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:
|
try:
|
||||||
await self.instance.install(self.version)
|
await self.instance.install(self.version)
|
||||||
except DockerBuildError as err:
|
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:
|
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:
|
if self.addon_store:
|
||||||
await self.sys_addons.data.update(self.addon_store)
|
await self.sys_addons.data.update(self.addon_store)
|
||||||
@@ -1145,8 +1141,9 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
await self.instance.run()
|
await self.instance.run()
|
||||||
except DockerError as err:
|
except DockerError as err:
|
||||||
|
_LOGGER.error("Could not start container for addon %s: %s", self.slug, err)
|
||||||
self.state = AddonState.ERROR
|
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())
|
return self.sys_create_task(self._wait_for_startup())
|
||||||
|
|
||||||
@@ -1161,8 +1158,9 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
await self.instance.stop()
|
await self.instance.stop()
|
||||||
except DockerError as err:
|
except DockerError as err:
|
||||||
|
_LOGGER.error("Could not stop container for addon %s: %s", self.slug, err)
|
||||||
self.state = AddonState.ERROR
|
self.state = AddonState.ERROR
|
||||||
raise AddonContainerStopUnknownError(addon=self.slug) from err
|
raise AddonUnknownError(addon=self.slug) from err
|
||||||
|
|
||||||
@Job(
|
@Job(
|
||||||
name="addon_restart",
|
name="addon_restart",
|
||||||
@@ -1200,7 +1198,10 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
return await self.instance.stats()
|
return await self.instance.stats()
|
||||||
except DockerError as err:
|
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(
|
@Job(
|
||||||
name="addon_write_stdin",
|
name="addon_write_stdin",
|
||||||
@@ -1218,7 +1219,10 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
await self.instance.write_stdin(data)
|
await self.instance.write_stdin(data)
|
||||||
except DockerError as err:
|
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:
|
async def _backup_command(self, command: str) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -1234,7 +1238,7 @@ class Addon(AddonModel):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Failed running pre-/post backup command %s: %s", command, err
|
"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(
|
@Job(
|
||||||
name="addon_begin_backup",
|
name="addon_begin_backup",
|
||||||
@@ -1323,18 +1327,14 @@ class Addon(AddonModel):
|
|||||||
try:
|
try:
|
||||||
self.instance.export_image(temp_path.joinpath("image.tar"))
|
self.instance.export_image(temp_path.joinpath("image.tar"))
|
||||||
except DockerError as err:
|
except DockerError as err:
|
||||||
raise AddonBackupExportImageUnknownError(
|
raise BackupRestoreUnknownError() from err
|
||||||
addon=self.slug
|
|
||||||
) from err
|
|
||||||
|
|
||||||
# Store local configs/state
|
# Store local configs/state
|
||||||
try:
|
try:
|
||||||
write_json_file(temp_path.joinpath("addon.json"), metadata)
|
write_json_file(temp_path.joinpath("addon.json"), metadata)
|
||||||
except ConfigurationFileError as err:
|
except ConfigurationFileError as err:
|
||||||
_LOGGER.error("Can't save meta for %s: %s", self.slug, err)
|
_LOGGER.error("Can't save meta for %s: %s", self.slug, err)
|
||||||
raise AddonCreateBackupMetadataFileUnknownError(
|
raise BackupRestoreUnknownError() from err
|
||||||
addon=self.slug
|
|
||||||
) from err
|
|
||||||
|
|
||||||
# Store AppArmor Profile
|
# Store AppArmor Profile
|
||||||
if apparmor_profile:
|
if apparmor_profile:
|
||||||
@@ -1344,9 +1344,7 @@ class Addon(AddonModel):
|
|||||||
apparmor_profile, profile_backup_file
|
apparmor_profile, profile_backup_file
|
||||||
)
|
)
|
||||||
except HostAppArmorError as err:
|
except HostAppArmorError as err:
|
||||||
raise AddonBackupAppArmorProfileUnknownError(
|
raise BackupRestoreUnknownError() from err
|
||||||
addon=self.slug
|
|
||||||
) from err
|
|
||||||
|
|
||||||
# Write tarfile
|
# Write tarfile
|
||||||
with tar_file as backup:
|
with tar_file as backup:
|
||||||
@@ -1401,7 +1399,7 @@ class Addon(AddonModel):
|
|||||||
_LOGGER.info("Finish backup for addon %s", self.slug)
|
_LOGGER.info("Finish backup for addon %s", self.slug)
|
||||||
except (tarfile.TarError, OSError, AddFileError) as err:
|
except (tarfile.TarError, OSError, AddFileError) as err:
|
||||||
_LOGGER.error("Can't write backup tarfile for addon %s: %s", self.slug, 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:
|
finally:
|
||||||
if was_running:
|
if was_running:
|
||||||
wait_for_start = await self.end_backup()
|
wait_for_start = await self.end_backup()
|
||||||
@@ -1444,9 +1442,9 @@ class Addon(AddonModel):
|
|||||||
tmp, data = await self.sys_run_in_executor(_extract_tarfile)
|
tmp, data = await self.sys_run_in_executor(_extract_tarfile)
|
||||||
except tarfile.TarError as err:
|
except tarfile.TarError as err:
|
||||||
_LOGGER.error("Can't extract backup tarfile for %s: %s", self.slug, 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:
|
except ConfigurationFileError as err:
|
||||||
raise AddonConfigurationFileUnknownError(addon=self.slug) from err
|
raise AddonUnknownError(addon=self.slug) from err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Validate
|
# Validate
|
||||||
@@ -1522,7 +1520,7 @@ class Addon(AddonModel):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Can't restore origin data for %s: %s", self.slug, err
|
"Can't restore origin data for %s: %s", self.slug, err
|
||||||
)
|
)
|
||||||
raise AddonRestoreBackupDataUnknownError(addon=self.slug) from err
|
raise BackupRestoreUnknownError() from err
|
||||||
|
|
||||||
# Restore AppArmor
|
# Restore AppArmor
|
||||||
profile_file = Path(tmp.name, "apparmor.txt")
|
profile_file = Path(tmp.name, "apparmor.txt")
|
||||||
@@ -1537,9 +1535,7 @@ class Addon(AddonModel):
|
|||||||
self.slug,
|
self.slug,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
raise AddonRestoreAppArmorProfileUnknownError(
|
raise BackupRestoreUnknownError() from err
|
||||||
addon=self.slug
|
|
||||||
) from err
|
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Is add-on loaded
|
# Is add-on loaded
|
||||||
|
|||||||
@@ -628,9 +628,6 @@ class Backup(JobGroup):
|
|||||||
if start_task := await self._addon_save(addon):
|
if start_task := await self._addon_save(addon):
|
||||||
start_tasks.append(start_task)
|
start_tasks.append(start_task)
|
||||||
except BackupError as err:
|
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)
|
self.sys_jobs.current.capture_error(err)
|
||||||
|
|
||||||
return start_tasks
|
return start_tasks
|
||||||
|
|||||||
@@ -358,26 +358,6 @@ class AddonConfigurationInvalidError(AddonConfigurationError, APIError):
|
|||||||
super().__init__(None, logger)
|
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):
|
class AddonBootConfigCannotChangeError(AddonsError, APIError):
|
||||||
"""Raise if user attempts to change addon boot config when it can't be changed."""
|
"""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)
|
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):
|
class AddonNotSupportedError(HassioNotSupportedError):
|
||||||
"""Addon doesn't support a function."""
|
"""Addon doesn't support a function."""
|
||||||
|
|
||||||
@@ -546,238 +504,11 @@ class AddonBuildArchitectureNotSupportedError(AddonNotSupportedError, APIError):
|
|||||||
super().__init__(None, logger)
|
super().__init__(None, logger)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable-next=too-many-ancestors
|
class AddonUnknownError(AddonsError, APIUnknownSupervisorError):
|
||||||
class AddonConfigurationFileUnknownError(
|
"""Raise when unknown error occurs taking an action for an addon."""
|
||||||
AddonConfigurationError, APIUnknownSupervisorError
|
|
||||||
):
|
|
||||||
"""Raise when unknown error occurs trying to read/write addon configuration file."""
|
|
||||||
|
|
||||||
error_key = "addon_configuration_file_unknown_error"
|
error_key = "addon_unknown_error"
|
||||||
message_template = (
|
message_template = "An unknown error occurred with addon {addon}"
|
||||||
"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"
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, logger: Callable[..., None] | None = None, *, addon: str
|
self, logger: Callable[..., None] | None = None, *, addon: str
|
||||||
@@ -1262,6 +993,55 @@ class BackupFileExistError(BackupError):
|
|||||||
"""Raise if the backup file already exists."""
|
"""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
|
# Security
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,13 +102,17 @@ class SupervisorJobError:
|
|||||||
"Unknown error, see Supervisor logs (check with 'ha supervisor logs')"
|
"Unknown error, see Supervisor logs (check with 'ha supervisor logs')"
|
||||||
)
|
)
|
||||||
stage: str | None = None
|
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 dictionary representation."""
|
||||||
return {
|
return {
|
||||||
"type": self.type_.__name__,
|
"type": self.type_.__name__,
|
||||||
"message": self.message,
|
"message": self.message,
|
||||||
"stage": self.stage,
|
"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:
|
def capture_error(self, err: HassioError | None = None) -> None:
|
||||||
"""Capture an error or record that an unknown error has occurred."""
|
"""Capture an error or record that an unknown error has occurred."""
|
||||||
if err:
|
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:
|
else:
|
||||||
new_error = SupervisorJobError(stage=self.stage)
|
new_error = SupervisorJobError(stage=self.stage)
|
||||||
self.errors += [new_error]
|
self.errors += [new_error]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from datetime import timedelta
|
|||||||
import errno
|
import errno
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import MagicMock, PropertyMock, call, patch
|
from unittest.mock import MagicMock, PropertyMock, call, patch
|
||||||
|
|
||||||
import aiodocker
|
import aiodocker
|
||||||
@@ -23,7 +24,13 @@ from supervisor.docker.addon import DockerAddon
|
|||||||
from supervisor.docker.const import ContainerState
|
from supervisor.docker.const import ContainerState
|
||||||
from supervisor.docker.manager import CommandReturn, DockerAPI
|
from supervisor.docker.manager import CommandReturn, DockerAPI
|
||||||
from supervisor.docker.monitor import DockerContainerStateEvent
|
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.hardware.helper import HwHelper
|
||||||
from supervisor.ingress import Ingress
|
from supervisor.ingress import Ingress
|
||||||
from supervisor.store.repository import Repository
|
from supervisor.store.repository import Repository
|
||||||
@@ -502,31 +509,26 @@ async def test_backup_with_pre_post_command(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"get_error,exception_on_exec",
|
("container_get_side_effect", "exec_run_side_effect", "exc_type_raised"),
|
||||||
[
|
[
|
||||||
(NotFound("missing"), False),
|
(NotFound("missing"), [(1, None)], AddonUnknownError),
|
||||||
(DockerException(), False),
|
(DockerException(), [(1, None)], AddonUnknownError),
|
||||||
(None, True),
|
(None, DockerException(), AddonUnknownError),
|
||||||
(None, False),
|
(None, [(1, None)], AddonPrePostBackupCommandReturnedError),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.usefixtures("tmp_supervisor_data", "path_extern")
|
||||||
async def test_backup_with_pre_command_error(
|
async def test_backup_with_pre_command_error(
|
||||||
coresys: CoreSys,
|
coresys: CoreSys,
|
||||||
install_addon_ssh: Addon,
|
install_addon_ssh: Addon,
|
||||||
container: MagicMock,
|
container: MagicMock,
|
||||||
get_error: DockerException | None,
|
container_get_side_effect: DockerException | None,
|
||||||
exception_on_exec: bool,
|
exec_run_side_effect: DockerException | list[tuple[int, Any]],
|
||||||
tmp_supervisor_data,
|
exc_type_raised: type[HassioError],
|
||||||
path_extern,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test backing up an addon with error running pre command."""
|
"""Test backing up an addon with error running pre command."""
|
||||||
if get_error:
|
coresys.docker.containers.get.side_effect = container_get_side_effect
|
||||||
coresys.docker.containers.get.side_effect = get_error
|
container.exec_run.side_effect = exec_run_side_effect
|
||||||
|
|
||||||
if exception_on_exec:
|
|
||||||
container.exec_run.side_effect = DockerException()
|
|
||||||
else:
|
|
||||||
container.exec_run.return_value = (1, None)
|
|
||||||
|
|
||||||
install_addon_ssh.path_data.mkdir()
|
install_addon_ssh.path_data.mkdir()
|
||||||
await install_addon_ssh.load()
|
await install_addon_ssh.load()
|
||||||
@@ -535,7 +537,7 @@ async def test_backup_with_pre_command_error(
|
|||||||
with (
|
with (
|
||||||
patch.object(DockerAddon, "is_running", return_value=True),
|
patch.object(DockerAddon, "is_running", return_value=True),
|
||||||
patch.object(Addon, "backup_pre", new=PropertyMock(return_value="backup_pre")),
|
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
|
assert await install_addon_ssh.backup(tarfile) is None
|
||||||
|
|
||||||
|
|||||||
@@ -590,9 +590,9 @@ async def test_addon_start_options_error(
|
|||||||
body = await resp.json()
|
body = await resp.json()
|
||||||
assert (
|
assert (
|
||||||
body["message"]
|
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"] == {
|
assert body["extra_fields"] == {
|
||||||
"addon": "local_example",
|
"addon": "local_example",
|
||||||
"logs_command": "ha supervisor logs",
|
"logs_command": "ha supervisor logs",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from supervisor.const import CoreState
|
|||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
from supervisor.docker.manager import DockerAPI
|
from supervisor.docker.manager import DockerAPI
|
||||||
from supervisor.exceptions import (
|
from supervisor.exceptions import (
|
||||||
|
AddonPrePostBackupCommandReturnedError,
|
||||||
AddonsError,
|
AddonsError,
|
||||||
BackupInvalidError,
|
BackupInvalidError,
|
||||||
HomeAssistantBackupError,
|
HomeAssistantBackupError,
|
||||||
@@ -24,6 +25,7 @@ from supervisor.exceptions import (
|
|||||||
from supervisor.homeassistant.core import HomeAssistantCore
|
from supervisor.homeassistant.core import HomeAssistantCore
|
||||||
from supervisor.homeassistant.module import HomeAssistant
|
from supervisor.homeassistant.module import HomeAssistant
|
||||||
from supervisor.homeassistant.websocket import HomeAssistantWebSocket
|
from supervisor.homeassistant.websocket import HomeAssistantWebSocket
|
||||||
|
from supervisor.jobs import SupervisorJob
|
||||||
from supervisor.mounts.mount import Mount
|
from supervisor.mounts.mount import Mount
|
||||||
from supervisor.supervisor import Supervisor
|
from supervisor.supervisor import Supervisor
|
||||||
|
|
||||||
@@ -401,6 +403,8 @@ async def test_api_backup_errors(
|
|||||||
"type": "BackupError",
|
"type": "BackupError",
|
||||||
"message": str(err),
|
"message": str(err),
|
||||||
"stage": None,
|
"stage": None,
|
||||||
|
"error_key": None,
|
||||||
|
"extra_fields": None,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
assert job["child_jobs"][2]["name"] == "backup_store_folders"
|
assert job["child_jobs"][2]["name"] == "backup_store_folders"
|
||||||
@@ -437,6 +441,8 @@ async def test_api_backup_errors(
|
|||||||
"type": "HomeAssistantBackupError",
|
"type": "HomeAssistantBackupError",
|
||||||
"message": "Backup error",
|
"message": "Backup error",
|
||||||
"stage": "home_assistant",
|
"stage": "home_assistant",
|
||||||
|
"error_key": None,
|
||||||
|
"extra_fields": None,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
assert job["child_jobs"][0]["name"] == "backup_store_homeassistant"
|
assert job["child_jobs"][0]["name"] == "backup_store_homeassistant"
|
||||||
@@ -445,6 +451,8 @@ async def test_api_backup_errors(
|
|||||||
"type": "HomeAssistantBackupError",
|
"type": "HomeAssistantBackupError",
|
||||||
"message": "Backup error",
|
"message": "Backup error",
|
||||||
"stage": None,
|
"stage": None,
|
||||||
|
"error_key": None,
|
||||||
|
"extra_fields": None,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
assert len(job["child_jobs"]) == 1
|
assert len(job["child_jobs"]) == 1
|
||||||
@@ -749,6 +757,8 @@ async def test_backup_to_multiple_locations_error_on_copy(
|
|||||||
"type": "BackupError",
|
"type": "BackupError",
|
||||||
"message": "Could not copy backup to .cloud_backup due to: ",
|
"message": "Could not copy backup to .cloud_backup due to: ",
|
||||||
"stage": None,
|
"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()
|
result = await resp.json()
|
||||||
assert len(result["data"]["backups"]) == 1
|
assert len(result["data"]["backups"]) == 1
|
||||||
assert result["data"]["backups"][0]["slug"] == "93b462f8"
|
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",
|
"type": "SupervisorError",
|
||||||
"message": "bad",
|
"message": "bad",
|
||||||
"stage": "test",
|
"stage": "test",
|
||||||
|
"error_key": None,
|
||||||
|
"extra_fields": None,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"child_jobs": [
|
"child_jobs": [
|
||||||
@@ -391,6 +393,8 @@ async def test_job_with_error(
|
|||||||
"type": "SupervisorError",
|
"type": "SupervisorError",
|
||||||
"message": "bad",
|
"message": "bad",
|
||||||
"stage": None,
|
"stage": None,
|
||||||
|
"error_key": None,
|
||||||
|
"extra_fields": None,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"child_jobs": [],
|
"child_jobs": [],
|
||||||
|
|||||||
Reference in New Issue
Block a user