Introduce pre-/post-snapshot commands (#2941)

Introduce commands which are run before and after a snapshot is being
taken. This is useful for e.g. for database add-ons to suspend write
operations during snapshot.
This commit is contained in:
Stefan Agner 2021-06-08 21:44:31 +02:00 committed by GitHub
parent 12487fb69d
commit 2f18c177ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 0 deletions

View File

@ -678,6 +678,21 @@ class Addon(AddonModel):
except DockerError as err: except DockerError as err:
raise AddonsError() from err raise AddonsError() from err
async def _snapshot_command(self, command: str) -> None:
try:
command_return = await self.instance.run_inside(command)
if command_return.exit_code != 0:
_LOGGER.error(
"Pre-/Post-Snapshot command returned error code: %s",
command_return.exit_code,
)
raise AddonsError()
except DockerError as err:
_LOGGER.error(
"Failed running pre-/post-snapshot command %s: %s", command, err
)
raise AddonsError() from err
async def snapshot(self, tar_file: tarfile.TarFile) -> None: async def snapshot(self, tar_file: tarfile.TarFile) -> None:
"""Snapshot state of an add-on.""" """Snapshot state of an add-on."""
with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp: with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp:
@ -729,12 +744,18 @@ class Addon(AddonModel):
arcname="data", arcname="data",
) )
if self.snapshot_pre is not None:
await self._snapshot_command(self.snapshot_pre)
try: try:
_LOGGER.info("Building snapshot for add-on %s", self.slug) _LOGGER.info("Building snapshot for add-on %s", self.slug)
await self.sys_run_in_executor(_write_tarfile) await self.sys_run_in_executor(_write_tarfile)
except (tarfile.TarError, OSError) as err: except (tarfile.TarError, OSError) as err:
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err) _LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
raise AddonsError() from err raise AddonsError() from err
finally:
if self.snapshot_post is not None:
await self._snapshot_command(self.snapshot_post)
_LOGGER.info("Finish snapshot for addon %s", self.slug) _LOGGER.info("Finish snapshot for addon %s", self.slug)

View File

@ -51,6 +51,8 @@ from ..const import (
ATTR_SERVICES, ATTR_SERVICES,
ATTR_SLUG, ATTR_SLUG,
ATTR_SNAPSHOT_EXCLUDE, ATTR_SNAPSHOT_EXCLUDE,
ATTR_SNAPSHOT_POST,
ATTR_SNAPSHOT_PRE,
ATTR_STAGE, ATTR_STAGE,
ATTR_STARTUP, ATTR_STARTUP,
ATTR_STDIN, ATTR_STDIN,
@ -358,6 +360,16 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return Exclude list for snapshot.""" """Return Exclude list for snapshot."""
return self.data.get(ATTR_SNAPSHOT_EXCLUDE, []) return self.data.get(ATTR_SNAPSHOT_EXCLUDE, [])
@property
def snapshot_pre(self) -> Optional[str]:
"""Return pre-snapshot command."""
return self.data.get(ATTR_SNAPSHOT_PRE)
@property
def snapshot_post(self) -> Optional[str]:
"""Return post-snapshot command."""
return self.data.get(ATTR_SNAPSHOT_POST)
@property @property
def default_init(self) -> bool: def default_init(self) -> bool:
"""Return True if the add-on have no own init.""" """Return True if the add-on have no own init."""

View File

@ -68,6 +68,8 @@ from ..const import (
ATTR_SERVICES, ATTR_SERVICES,
ATTR_SLUG, ATTR_SLUG,
ATTR_SNAPSHOT_EXCLUDE, ATTR_SNAPSHOT_EXCLUDE,
ATTR_SNAPSHOT_POST,
ATTR_SNAPSHOT_PRE,
ATTR_SQUASH, ATTR_SQUASH,
ATTR_STAGE, ATTR_STAGE,
ATTR_STARTUP, ATTR_STARTUP,
@ -280,6 +282,8 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)], vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)],
vol.Optional(ATTR_DISCOVERY): [valid_discovery_service], vol.Optional(ATTR_DISCOVERY): [valid_discovery_service],
vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str], vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str],
vol.Optional(ATTR_SNAPSHOT_PRE): str,
vol.Optional(ATTR_SNAPSHOT_POST): str,
vol.Optional(ATTR_OPTIONS, default={}): dict, vol.Optional(ATTR_OPTIONS, default={}): dict,
vol.Optional(ATTR_SCHEMA, default={}): vol.Any( vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
vol.Schema( vol.Schema(

View File

@ -270,6 +270,8 @@ ATTR_SIGNAL = "signal"
ATTR_SIZE = "size" ATTR_SIZE = "size"
ATTR_SLUG = "slug" ATTR_SLUG = "slug"
ATTR_SNAPSHOT_EXCLUDE = "snapshot_exclude" ATTR_SNAPSHOT_EXCLUDE = "snapshot_exclude"
ATTR_SNAPSHOT_PRE = "snapshot_pre"
ATTR_SNAPSHOT_POST = "snapshot_post"
ATTR_SNAPSHOTS = "snapshots" ATTR_SNAPSHOTS = "snapshots"
ATTR_SOURCE = "source" ATTR_SOURCE = "source"
ATTR_SQUASH = "squash" ATTR_SQUASH = "squash"