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.apparmor import adjust_profile
|
||||||
from ..utils.json import read_json_file, write_json_file
|
from ..utils.json import read_json_file, write_json_file
|
||||||
from ..utils.tar import atomic_contents_add, secure_path
|
from ..utils.tar import atomic_contents_add, secure_path
|
||||||
|
from .const import SnapshotAddonMode
|
||||||
from .model import AddonModel, Data
|
from .model import AddonModel, Data
|
||||||
from .options import AddonOptions
|
from .options import AddonOptions
|
||||||
from .utils import remove_data
|
from .utils import remove_data
|
||||||
@ -695,6 +696,8 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
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."""
|
||||||
|
is_running = await self.is_running()
|
||||||
|
|
||||||
with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp:
|
with TemporaryDirectory(dir=self.sys_config.path_tmp) as temp:
|
||||||
temp_path = Path(temp)
|
temp_path = Path(temp)
|
||||||
|
|
||||||
@ -744,8 +747,15 @@ class Addon(AddonModel):
|
|||||||
arcname="data",
|
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)
|
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:
|
try:
|
||||||
_LOGGER.info("Building snapshot for add-on %s", self.slug)
|
_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)
|
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
|
||||||
raise AddonsError() from err
|
raise AddonsError() from err
|
||||||
finally:
|
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)
|
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)
|
_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 awesomeversion import AwesomeVersion, AwesomeVersionException
|
||||||
|
|
||||||
|
from supervisor.addons.const import SnapshotAddonMode
|
||||||
|
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ATTR_ADVANCED,
|
ATTR_ADVANCED,
|
||||||
ATTR_APPARMOR,
|
ATTR_APPARMOR,
|
||||||
@ -76,6 +78,7 @@ from ..const import (
|
|||||||
)
|
)
|
||||||
from ..coresys import CoreSys, CoreSysAttributes
|
from ..coresys import CoreSys, CoreSysAttributes
|
||||||
from ..docker.const import Capabilities
|
from ..docker.const import Capabilities
|
||||||
|
from .const import ATTR_SNAPSHOT
|
||||||
from .options import AddonOptions, UiOptions
|
from .options import AddonOptions, UiOptions
|
||||||
from .validate import RE_SERVICE, RE_VOLUME
|
from .validate import RE_SERVICE, RE_VOLUME
|
||||||
|
|
||||||
@ -370,6 +373,11 @@ class AddonModel(CoreSysAttributes, ABC):
|
|||||||
"""Return post-snapshot command."""
|
"""Return post-snapshot command."""
|
||||||
return self.data.get(ATTR_SNAPSHOT_POST)
|
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
|
@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."""
|
||||||
|
@ -7,6 +7,8 @@ import uuid
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from supervisor.addons.const import SnapshotAddonMode
|
||||||
|
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ARCH_ALL,
|
ARCH_ALL,
|
||||||
ATTR_ACCESS_TOKEN,
|
ATTR_ACCESS_TOKEN,
|
||||||
@ -107,6 +109,7 @@ from ..validate import (
|
|||||||
uuid_match,
|
uuid_match,
|
||||||
version_tag,
|
version_tag,
|
||||||
)
|
)
|
||||||
|
from .const import ATTR_SNAPSHOT
|
||||||
from .options import RE_SCHEMA_ELEMENT
|
from .options import RE_SCHEMA_ELEMENT
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
@ -161,6 +164,14 @@ def _warn_addon_config(config: Dict[str, Any]):
|
|||||||
name,
|
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
|
return config
|
||||||
|
|
||||||
|
|
||||||
@ -284,6 +295,9 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
|
|||||||
vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str],
|
vol.Optional(ATTR_SNAPSHOT_EXCLUDE): [str],
|
||||||
vol.Optional(ATTR_SNAPSHOT_PRE): str,
|
vol.Optional(ATTR_SNAPSHOT_PRE): str,
|
||||||
vol.Optional(ATTR_SNAPSHOT_POST): 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_OPTIONS, default={}): dict,
|
||||||
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
|
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
|
||||||
vol.Schema(
|
vol.Schema(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user