mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-08 17:56:33 +00:00
Support HOT/COLD snapshots for Add-ons (#2943)
* Support HOT/COLD snapshots for Add-ons * Apply suggestions from code review Co-authored-by: Joakim Sørensen <joasoe@gmail.com> * Add warning Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
This commit is contained in:
parent
16f2f63081
commit
0177b38ded
@ -65,6 +65,7 @@ from ..utils import check_port
|
||||
from ..utils.apparmor import adjust_profile
|
||||
from ..utils.json import read_json_file, write_json_file
|
||||
from ..utils.tar import atomic_contents_add, secure_path
|
||||
from .const import SnapshotAddonMode
|
||||
from .model import AddonModel, Data
|
||||
from .options import AddonOptions
|
||||
from .utils import remove_data
|
||||
@ -695,6 +696,8 @@ class Addon(AddonModel):
|
||||
|
||||
async def snapshot(self, tar_file: tarfile.TarFile) -> None:
|
||||
"""Snapshot state of an add-on."""
|
||||
is_running = await self.is_running()
|
||||
|
||||
with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp:
|
||||
temp_path = Path(temp)
|
||||
|
||||
@ -744,8 +747,15 @@ class Addon(AddonModel):
|
||||
arcname="data",
|
||||
)
|
||||
|
||||
if self.snapshot_pre is not None:
|
||||
if (
|
||||
is_running
|
||||
and self.snapshot_mode == SnapshotAddonMode.HOT
|
||||
and self.snapshot_pre is not None
|
||||
):
|
||||
await self._snapshot_command(self.snapshot_pre)
|
||||
elif is_running and self.snapshot_mode == SnapshotAddonMode.COLD:
|
||||
_LOGGER.info("Shutdown add-on %s for cold snapshot", self.slug)
|
||||
await self.instance.stop()
|
||||
|
||||
try:
|
||||
_LOGGER.info("Building snapshot for add-on %s", self.slug)
|
||||
@ -754,8 +764,15 @@ class Addon(AddonModel):
|
||||
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
|
||||
raise AddonsError() from err
|
||||
finally:
|
||||
if self.snapshot_post is not None:
|
||||
if (
|
||||
is_running
|
||||
and self.snapshot_mode == SnapshotAddonMode.HOT
|
||||
and self.snapshot_post is not None
|
||||
):
|
||||
await self._snapshot_command(self.snapshot_post)
|
||||
elif is_running and self.snapshot_mode is SnapshotAddonMode.COLD:
|
||||
_LOGGER.info("Starting add-on %s again", self.slug)
|
||||
await self.start()
|
||||
|
||||
_LOGGER.info("Finish snapshot for addon %s", self.slug)
|
||||
|
||||
|
12
supervisor/addons/const.py
Normal file
12
supervisor/addons/const.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""Add-on static data."""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class SnapshotAddonMode(str, Enum):
|
||||
"""Snapshot mode of an Add-on."""
|
||||
|
||||
HOT = "hot"
|
||||
COLD = "cold"
|
||||
|
||||
|
||||
ATTR_SNAPSHOT = "snapshot"
|
@ -5,6 +5,8 @@ from typing import Any, Awaitable, Dict, List, Optional
|
||||
|
||||
from awesomeversion import AwesomeVersion, AwesomeVersionException
|
||||
|
||||
from supervisor.addons.const import SnapshotAddonMode
|
||||
|
||||
from ..const import (
|
||||
ATTR_ADVANCED,
|
||||
ATTR_APPARMOR,
|
||||
@ -76,6 +78,7 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from ..docker.const import Capabilities
|
||||
from .const import ATTR_SNAPSHOT
|
||||
from .options import AddonOptions, UiOptions
|
||||
from .validate import RE_SERVICE, RE_VOLUME
|
||||
|
||||
@ -370,6 +373,11 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
"""Return post-snapshot command."""
|
||||
return self.data.get(ATTR_SNAPSHOT_POST)
|
||||
|
||||
@property
|
||||
def snapshot_mode(self) -> SnapshotAddonMode:
|
||||
"""Return if snapshot is hot/cold."""
|
||||
return self.data[ATTR_SNAPSHOT]
|
||||
|
||||
@property
|
||||
def default_init(self) -> bool:
|
||||
"""Return True if the add-on have no own init."""
|
||||
|
@ -7,6 +7,8 @@ import uuid
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from supervisor.addons.const import SnapshotAddonMode
|
||||
|
||||
from ..const import (
|
||||
ARCH_ALL,
|
||||
ATTR_ACCESS_TOKEN,
|
||||
@ -107,6 +109,7 @@ from ..validate import (
|
||||
uuid_match,
|
||||
version_tag,
|
||||
)
|
||||
from .const import ATTR_SNAPSHOT
|
||||
from .options import RE_SCHEMA_ELEMENT
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -161,6 +164,14 @@ def _warn_addon_config(config: Dict[str, Any]):
|
||||
name,
|
||||
)
|
||||
|
||||
if config.get(ATTR_SNAPSHOT, SnapshotAddonMode.HOT) == SnapshotAddonMode.COLD and (
|
||||
config.get(ATTR_SNAPSHOT_POST) or config.get(ATTR_SNAPSHOT_PRE)
|
||||
):
|
||||
_LOGGER.warning(
|
||||
"Add-on which only support COLD backups trying to use post/pre commands. Please report this to the maintainer of %s",
|
||||
name,
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@ -284,6 +295,9 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
|
||||
vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str],
|
||||
vol.Optional(ATTR_SNAPSHOT_PRE): str,
|
||||
vol.Optional(ATTR_SNAPSHOT_POST): str,
|
||||
vol.Optional(ATTR_SNAPSHOT, default=SnapshotAddonMode.HOT): vol.Coerce(
|
||||
SnapshotAddonMode
|
||||
),
|
||||
vol.Optional(ATTR_OPTIONS, default={}): dict,
|
||||
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
|
||||
vol.Schema(
|
||||
|
Loading…
x
Reference in New Issue
Block a user