Compare commits

...

15 Commits

Author SHA1 Message Date
ludeeus
de7ef86f52 Disallow ' 2023-06-16 12:33:04 +00:00
ludeeus
6f614c91d7 Quote CIFS password to remove strict requirements 2023-06-15 16:57:53 +00:00
Joakim Sørensen
8b4e8e9804 Add backup location to info/list API calls (#4346)
* Add backup location to info/list API calls

* Requested adjustments
2023-06-15 16:00:37 +02:00
cociweb
5d1ef34f17 Fix propagation mode of mounts to allow Ha-Core to start (#4374)
* Fix propagation mode of mounts to allow Ha-Core to start

Change the mount propagation mode from SLAVE to RSLAVE in case of /media and /share

* Check /share and /media to use RSLAVE as a propagation mode of mount

* Fix mount propagation mode of /share and /media

* Check /share and /media for rslave propagation mode
2023-06-14 17:40:46 -04:00
Matheson Steplock
9504eff889 Support Debian Bookworm (#4377) 2023-06-14 20:31:30 +02:00
dependabot[bot]
d5828a6815 Bump pre-commit from 3.3.2 to 3.3.3 (#4380)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 11:13:00 +02:00
dependabot[bot]
488f246f75 Bump urllib3 from 1.26.15 to 2.0.3 (#4351)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.15 to 2.0.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.15...2.0.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 16:43:53 -04:00
dependabot[bot]
000d4ec78a Bump pyupgrade from 3.4.0 to 3.6.0 (#4372)
Bumps [pyupgrade](https://github.com/asottile/pyupgrade) from 3.4.0 to 3.6.0.
- [Commits](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0)

---
updated-dependencies:
- dependency-name: pyupgrade
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 16:18:54 -04:00
Stefan Agner
6c0415163b Fix Audio mounts for Add-ons (#4379) 2023-06-13 12:30:58 +02:00
dependabot[bot]
0205cbb78b Bump dessant/lock-threads from 4.0.0 to 4.0.1 (#4378)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-13 10:35:16 +02:00
dependabot[bot]
72db559adc Bump home-assistant/builder from 2023.03.0 to 2023.06.0 (#4369)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-12 14:51:35 +02:00
dependabot[bot]
a57c145870 Bump actions/checkout from 3.5.2 to 3.5.3 (#4370)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-12 12:04:44 +02:00
dependabot[bot]
759fd1077a Bump pytest from 7.3.1 to 7.3.2 (#4371)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-12 11:51:05 +02:00
dependabot[bot]
fb90e6d07e Bump sentry-sdk from 1.25.0 to 1.25.1 (#4350)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 11:04:23 +02:00
dependabot[bot]
86d17acd83 Bump docker/login-action from 2.1.0 to 2.2.0 (#4349)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 10:52:47 +02:00
19 changed files with 97 additions and 50 deletions

View File

@@ -53,7 +53,7 @@ jobs:
requirements: ${{ steps.requirements.outputs.changed }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
with:
fetch-depth: 0
@@ -88,7 +88,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
with:
fetch-depth: 0
@@ -121,14 +121,14 @@ jobs:
- name: Login to DockerHub
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v2.1.0
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v2.1.0
uses: docker/login-action@v2.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -139,7 +139,7 @@ jobs:
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
- name: Build supervisor
uses: home-assistant/builder@2023.03.0
uses: home-assistant/builder@2023.06.0
with:
args: |
$BUILD_ARGS \
@@ -156,7 +156,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
with:
fetch-depth: 0
@@ -195,7 +195,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Initialize git
if: needs.init.outputs.publish == 'true'
@@ -220,11 +220,11 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Build the Supervisor
if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2023.03.0
uses: home-assistant/builder@2023.06.0
with:
args: |
--test \

View File

@@ -26,7 +26,7 @@ jobs:
name: Prepare Python dependencies
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python
id: python
uses: actions/setup-python@v4.6.1
@@ -67,7 +67,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -96,7 +96,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Register hadolint problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
@@ -111,7 +111,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -155,7 +155,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -187,7 +187,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -228,7 +228,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -272,7 +272,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -304,7 +304,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -345,7 +345,7 @@ jobs:
name: Run tests Python ${{ needs.prepare.outputs.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python
@@ -403,7 +403,7 @@ jobs:
needs: ["pytest", "prepare"]
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v4.6.1
id: python

View File

@@ -9,7 +9,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4.0.0
- uses: dessant/lock-threads@v4.0.1
with:
github-token: ${{ github.token }}
issue-inactive-days: "30"

View File

@@ -11,7 +11,7 @@ jobs:
name: Release Drafter
steps:
- name: Checkout the repository
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
with:
fetch-depth: 0

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
uses: actions/checkout@v3.5.3
- name: Sentry Release
uses: getsentry/action-release@v1.4.1
env:

View File

@@ -20,7 +20,7 @@ pulsectl==23.5.2
pyudev==0.24.1
ruamel.yaml==0.17.21
securetar==2023.3.0
sentry-sdk==1.25.0
sentry-sdk==1.25.1
voluptuous==0.13.1
dbus-fast==1.86.0
typing_extensions==4.6.3

View File

@@ -2,15 +2,15 @@ black==23.3.0
coverage==7.2.7
flake8-docstrings==1.7.0
flake8==6.0.0
pre-commit==3.3.2
pre-commit==3.3.3
pydocstyle==6.3.0
pylint==2.17.4
pytest-aiohttp==1.0.4
pytest-asyncio==0.18.3
pytest-cov==4.1.0
pytest-timeout==2.1.0
pytest==7.3.1
pyupgrade==3.4.0
pytest==7.3.2
pyupgrade==3.6.0
time-machine==2.9.0
typing_extensions==4.6.3
urllib3==1.26.15
urllib3==2.0.3

View File

@@ -100,6 +100,7 @@ class APIBackups(CoreSysAttributes):
ATTR_DATE: backup.date,
ATTR_TYPE: backup.sys_type,
ATTR_SIZE: backup.size,
ATTR_LOCATON: backup.location,
ATTR_PROTECTED: backup.protected,
ATTR_COMPRESSED: backup.compressed,
ATTR_CONTENT: {
@@ -172,6 +173,7 @@ class APIBackups(CoreSysAttributes):
ATTR_PROTECTED: backup.protected,
ATTR_SUPERVISOR_VERSION: backup.supervisor_version,
ATTR_HOMEASSISTANT: backup.homeassistant_version,
ATTR_LOCATON: backup.location,
ATTR_ADDONS: data_addons,
ATTR_REPOSITORIES: backup.repositories,
ATTR_FOLDERS: backup.folders,

View File

@@ -2,6 +2,7 @@
from base64 import b64decode, b64encode
from collections.abc import Awaitable
from datetime import timedelta
from functools import cached_property
import json
import logging
from pathlib import Path
@@ -150,6 +151,14 @@ class Backup(CoreSysAttributes):
"""Set the Docker config data."""
self._data[ATTR_DOCKER] = value
@cached_property
def location(self) -> str | None:
"""Return the location of the backup."""
for backup_mount in self.sys_mounts.backup_mounts:
if self.tarfile.is_relative_to(backup_mount.local_where):
return backup_mount.name
return None
@property
def size(self):
"""Return backup size."""

View File

@@ -387,7 +387,7 @@ class DockerAddon(DockerInterface):
source=self.sys_config.path_extern_share.as_posix(),
target="/share",
read_only=addon_mapping[MAP_SHARE],
propagation=PropagationMode.SLAVE.value,
propagation=PropagationMode.RSLAVE.value,
)
)
@@ -398,7 +398,7 @@ class DockerAddon(DockerInterface):
source=self.sys_config.path_extern_media.as_posix(),
target="/media",
read_only=addon_mapping[MAP_MEDIA],
propagation=PropagationMode.SLAVE.value,
propagation=PropagationMode.RSLAVE.value,
)
)
@@ -457,7 +457,7 @@ class DockerAddon(DockerInterface):
mounts += [
Mount(
type=MountType.BIND.value,
source=self.sys_homeassistant.path_extern_pulse.as_posix(),
source=self.addon.path_extern_pulse.as_posix(),
target="/etc/pulse/client.conf",
read_only=True,
),

View File

@@ -97,14 +97,14 @@ class DockerHomeAssistant(DockerInterface):
source=self.sys_config.path_extern_share.as_posix(),
target="/share",
read_only=False,
propagation=PropagationMode.SLAVE.value,
propagation=PropagationMode.RSLAVE.value,
),
Mount(
type=MountType.BIND.value,
source=self.sys_config.path_extern_media.as_posix(),
target="/media",
read_only=False,
propagation=PropagationMode.SLAVE.value,
propagation=PropagationMode.RSLAVE.value,
),
# Configuration audio
Mount(

View File

@@ -358,7 +358,8 @@ class CIFSMount(NetworkMount):
def options(self) -> list[str]:
"""Options to use to mount."""
return (
super().options + [f"username={self.username}", f"password={self.password}"]
super().options
+ [f"username={self.username}", f"password='{self.password}'"]
if self.username
else []
)

View File

@@ -21,13 +21,12 @@ from .const import (
RE_MOUNT_NAME = re.compile(r"^\w+$")
RE_PATH_PART = re.compile(r"^[^\\\/]+")
RE_MOUNT_OPTION = re.compile(r"^[^,=]+$")
RE_MOUNT_OPTION = re.compile(r"^[^']+$")
VALIDATE_NAME = vol.Match(RE_MOUNT_NAME)
VALIDATE_SERVER = vol.Match(RE_PATH_PART)
VALIDATE_SHARE = vol.Match(RE_PATH_PART)
VALIDATE_USERNAME = vol.Match(RE_MOUNT_OPTION)
VALIDATE_PASSWORD = vol.Match(RE_MOUNT_OPTION)
_SCHEMA_BASE_MOUNT_CONFIG = vol.Schema(
{
@@ -49,8 +48,8 @@ SCHEMA_MOUNT_CIFS = _SCHEMA_MOUNT_NETWORK.extend(
{
vol.Required(ATTR_TYPE): MountType.CIFS.value,
vol.Required(ATTR_SHARE): VALIDATE_SHARE,
vol.Inclusive(ATTR_USERNAME, "basic_auth"): VALIDATE_USERNAME,
vol.Inclusive(ATTR_PASSWORD, "basic_auth"): VALIDATE_PASSWORD,
vol.Inclusive(ATTR_USERNAME, "basic_auth"): vol.Match(RE_MOUNT_OPTION),
vol.Inclusive(ATTR_PASSWORD, "basic_auth"): vol.Match(RE_MOUNT_OPTION),
}
)

View File

@@ -12,7 +12,7 @@ from .base import CheckBase
def _check_container(container: DockerInterface) -> bool:
"""Return true if container has a config issue."""
return any(
mount.get("Propagation") != PropagationMode.SLAVE.value
mount.get("Propagation") != PropagationMode.RSLAVE.value
for mount in container.meta_mounts
if mount.get("Destination") in ["/media", "/share"]
)

View File

@@ -5,7 +5,7 @@ from ...coresys import CoreSys
from ..const import UnsupportedReason
from .base import EvaluateBase
SUPPORTED_OS = ["Debian GNU/Linux 11 (bullseye)"]
SUPPORTED_OS = ["Debian GNU/Linux 11 (bullseye)", "Debian GNU/Linux 12 (bookworm)"]
def setup(coresys: CoreSys) -> EvaluateBase:

View File

@@ -95,6 +95,11 @@ async def test_backup_to_location(
assert (tmp_supervisor_data / backup_dir / f"{slug}.tar").exists()
resp = await api_client.get(f"/backups/{slug}/info")
result = await resp.json()
assert result["result"] == "ok"
assert result["data"]["location"] == location
async def test_backup_to_default(
api_client: TestClient,

View File

@@ -121,7 +121,7 @@ def test_addon_map_folder_defaults(
source=coresys.config.path_extern_media.as_posix(),
target="/media",
read_only=True,
propagation="slave",
propagation="rslave",
)
in docker_addon.mounts
)
@@ -133,7 +133,7 @@ def test_addon_map_folder_defaults(
source=coresys.config.path_extern_share.as_posix(),
target="/share",
read_only=True,
propagation="slave",
propagation="rslave",
)
in docker_addon.mounts
)

View File

@@ -39,7 +39,7 @@ async def test_cifs_mount(
"server": "test.local",
"share": "camera",
"username": "admin",
"password": "password",
"password": "p@assword!,=",
}
mount: CIFSMount = Mount.from_dict(coresys, mount_data)
@@ -54,7 +54,7 @@ async def test_cifs_mount(
assert mount.what == "//test.local/camera"
assert mount.where == Path("/mnt/data/supervisor/mounts/test")
assert mount.local_where == tmp_supervisor_data / "mounts" / "test"
assert mount.options == ["username=admin", "password=password"]
assert mount.options == ["username=admin", "password='p@assword!,='"]
assert not mount.local_where.exists()
assert mount.to_dict(skip_secrets=False) == mount_data
@@ -73,7 +73,7 @@ async def test_cifs_mount(
"mnt-data-supervisor-mounts-test.mount",
"fail",
[
["Options", Variant("s", "username=admin,password=password")],
["Options", Variant("s", "username=admin,password='p@assword!,='")],
["Type", Variant("s", "cifs")],
["Description", Variant("s", "Supervisor cifs mount: test")],
["What", Variant("s", "//test.local/camera")],

View File

@@ -1,5 +1,7 @@
"""Tests for mount manager validation."""
import re
import pytest
from voluptuous import Invalid
@@ -15,6 +17,8 @@ async def test_valid_mounts():
"type": "cifs",
"server": "test.local",
"share": "test",
"username": "admin",
"password": "p@assword!,=",
}
)
@@ -77,12 +81,39 @@ async def test_invalid_cifs():
SCHEMA_MOUNT_CONFIG(base)
# Path is for NFS
with pytest.raises(Invalid):
SCHEMA_MOUNT_CONFIG({"path": "backups"})
with pytest.raises(
Invalid, match=re.escape("required key not provided @ data['share']")
):
SCHEMA_MOUNT_CONFIG({**base, "path": "backups"})
# Username and password must be together
with pytest.raises(Invalid):
SCHEMA_MOUNT_CONFIG({"username": "admin"})
with pytest.raises(
Invalid,
match=re.escape(
"some but not all values in the same group of inclusion 'basic_auth' @ data[<basic_auth>]"
),
):
SCHEMA_MOUNT_CONFIG({**base, "share": "test", "username": "admin"})
# Username and password must be together
with pytest.raises(
Invalid,
match=re.escape(
"some but not all values in the same group of inclusion 'basic_auth' @ data[<basic_auth>]"
),
):
SCHEMA_MOUNT_CONFIG({**base, "share": "test", "password": "my=!pass"})
# Invalid password
with pytest.raises(
Invalid,
match=re.escape(
"does not match regular expression ^[^']+$ for dictionary value @ data['password']"
),
):
SCHEMA_MOUNT_CONFIG(
{**base, "share": "test", "username": "admin", "password": "my=!pa'ss,"}
)
async def test_invalid_nfs():