Compare commits

...

43 Commits

Author SHA1 Message Date
dependabot[bot]
ad85fa29b6 Bump pylint from 2.12.2 to 2.13.4 (#3539)
* Bump pylint from 2.12.2 to 2.13.4

Bumps [pylint](https://github.com/PyCQA/pylint) from 2.12.2 to 2.13.4.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.12.2...v2.13.4)

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

Signed-off-by: dependabot[bot] <support@github.com>

* Explicitly provide maxsize in lru_cache decorator

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-04-04 12:52:33 -04:00
dependabot[bot]
f57aeab9ae Bump pre-commit from 2.17.0 to 2.18.1 (#3543)
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.17.0 to 2.18.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.17.0...v2.18.1)

---
updated-dependencies:
- dependency-name: pre-commit
  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>
2022-04-04 12:15:01 -04:00
dependabot[bot]
383ea277b7 Bump actions/setup-python from 3.0.0 to 3.1.0 (#3541)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  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>
2022-04-04 12:10:24 -04:00
dependabot[bot]
a32d1668ee Bump awesomeversion from 22.2.0 to 22.4.0 (#3542)
Bumps [awesomeversion](https://github.com/ludeeus/awesomeversion) from 22.2.0 to 22.4.0.
- [Release notes](https://github.com/ludeeus/awesomeversion/releases)
- [Commits](https://github.com/ludeeus/awesomeversion/compare/22.2.0...22.4.0)

---
updated-dependencies:
- dependency-name: awesomeversion
  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>
2022-04-04 12:08:44 -04:00
Pascal Vizeli
e445a8aabf Enable CAS for Add-ons (#3506)
* Revert "Disable CAS until it work (#3504)"

This reverts commit 435241bccf.

* Revert exception that are not forwarded

* enable for add-ons

* Apply suggestions from code review

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* fix black

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-03-31 10:59:34 -04:00
dependabot[bot]
0de190268f Bump voluptuous from 0.12.2 to 0.13.0 (#3538)
Bumps [voluptuous](https://github.com/alecthomas/voluptuous) from 0.12.2 to 0.13.0.
- [Release notes](https://github.com/alecthomas/voluptuous/releases)
- [Changelog](https://github.com/alecthomas/voluptuous/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alecthomas/voluptuous/compare/0.12.2...0.13.0)

---
updated-dependencies:
- dependency-name: voluptuous
  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>
2022-03-31 16:55:13 +02:00
dependabot[bot]
9e5101aa39 Bump actions/cache from 3.0.0 to 3.0.1 (#3537)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-31 14:21:01 +02:00
Mike Degatano
e2ac5042d8 Apply network settings on start to fix defaults (#3528)
* Apply network settings on start to fix defaults

* Use job check and add tests
2022-03-31 12:14:40 +02:00
dependabot[bot]
bfe1cb073c Bump pulsectl from 22.1.3 to 22.3.2 (#3527)
Bumps [pulsectl](https://github.com/mk-fg/python-pulse-control) from 22.1.3 to 22.3.2.
- [Release notes](https://github.com/mk-fg/python-pulse-control/releases)
- [Changelog](https://github.com/mk-fg/python-pulse-control/blob/master/CHANGES.rst)
- [Commits](https://github.com/mk-fg/python-pulse-control/commits)

---
updated-dependencies:
- dependency-name: pulsectl
  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>
2022-03-29 20:12:13 -04:00
dependabot[bot]
3a1364dfcd Bump black from 22.1.0 to 22.3.0 (#3535)
Bumps [black](https://github.com/psf/black) from 22.1.0 to 22.3.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.1.0...22.3.0)

---
updated-dependencies:
- dependency-name: black
  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>
2022-03-29 18:53:45 -04:00
dependabot[bot]
3f63414bb3 Bump debugpy from 1.5.1 to 1.6.0 (#3530)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.5.1...v1.6.0)

---
updated-dependencies:
- dependency-name: debugpy
  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>
2022-03-29 10:26:35 +02:00
dependabot[bot]
8b3a09e5b8 Bump jinja2 from 3.0.3 to 3.1.1 (#3534)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.3 to 3.1.1.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.0.3...3.1.1)

---
updated-dependencies:
- dependency-name: jinja2
  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>
2022-03-29 10:26:16 +02:00
dependabot[bot]
ca7dc8113b Bump actions/cache from 2.1.7 to 3 (#3525)
* Bump actions/cache from 2.1.7 to 3

Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.7...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix 3 -> 3.0.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2022-03-22 14:01:15 +01:00
dependabot[bot]
6d2a603cf9 Bump sentry-sdk from 1.5.7 to 1.5.8 (#3523)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-21 08:13:38 +01:00
dependabot[bot]
d536ac8604 Bump pytest from 7.1.0 to 7.1.1 (#3522)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-18 08:42:05 +01:00
Pascal Vizeli
c67317571c Fix issues with old protection flag (#3521)
* Fix issues with old protection flag

* fix typing
2022-03-16 13:15:56 +01:00
dependabot[bot]
d93def7f22 Bump cryptography from 36.0.1 to 36.0.2 (#3520)
Bumps [cryptography](https://github.com/pyca/cryptography) from 36.0.1 to 36.0.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/36.0.1...36.0.2)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-16 11:23:21 +01:00
dependabot[bot]
20e45e3c00 Bump pytest from 7.0.1 to 7.1.0 (#3516)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.0.1 to 7.1.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.0.1...7.1.0)

---
updated-dependencies:
- dependency-name: pytest
  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>
2022-03-14 17:42:07 +01:00
dependabot[bot]
5758d42c91 Bump pyupgrade from 2.31.0 to 2.31.1 (#3515)
Bumps [pyupgrade](https://github.com/asottile/pyupgrade) from 2.31.0 to 2.31.1.
- [Release notes](https://github.com/asottile/pyupgrade/releases)
- [Commits](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1)

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

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-14 13:38:15 +01:00
Joakim Sørensen
d2dc78ae6a Add backup and restore to CI (#3509)
* Add backup and restore to CI

* Update .github/workflows/builder.yml

* -r

* restore ssl

* restart between

* Make sure it actually installed

* access .data
2022-03-11 09:43:50 -05:00
Joakim Sørensen
3fd3c02010 Remove name argument to _restore (#3508) 2022-03-11 09:31:22 -05:00
Joakim Sørensen
a82b4aa6c8 Get arch from config and not data when generating image (#3507) 2022-03-11 09:27:34 -05:00
Pascal Vizeli
45e54d93c7 Remove timeout on validate for docker (#3505) 2022-03-10 14:11:18 +01:00
Pascal Vizeli
435241bccf Disable CAS until it work (#3504)
* Disable CAS until it work

* fix tests
2022-03-10 13:18:52 +01:00
Pascal Vizeli
1b8558ced3 Reduce floot on startup (#3500)
* Reduce floot on startup

* Stop loading

* Update supervisor/resolution/checks/core_trust.py

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

* Update supervisor/resolution/checks/plugin_trust.py

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

* Update supervisor/resolution/checks/core_trust.py

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2022-03-10 10:42:18 +01:00
dependabot[bot]
4339cae241 Bump sentry-sdk from 1.5.6 to 1.5.7 (#3494)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.5.6 to 1.5.7.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.5.6...1.5.7)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-09 12:53:56 +01:00
Joakim Sørensen
4f2469fd98 Add collapse-after for dependencies (#3493)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-03-08 08:19:16 +01:00
dependabot[bot]
a90e8be6bc Bump actions/stale from 4.1.0 to 5.0.0 (#3492)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2022-03-08 07:50:16 +01:00
dependabot[bot]
dcaf36a8e5 Bump release-drafter/release-drafter from 5.18.1 to 5.19.0 (#3491)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-08 07:48:42 +01:00
dependabot[bot]
908df3b234 Bump home-assistant/builder from 2022.01.0 to 2022.03.1 (#3481)
Bumps [home-assistant/builder](https://github.com/home-assistant/builder) from 2022.01.0 to 2022.03.1.
- [Release notes](https://github.com/home-assistant/builder/releases)
- [Commits](https://github.com/home-assistant/builder/compare/2022.01.0...2022.03.1)

---
updated-dependencies:
- dependency-name: home-assistant/builder
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-07 12:41:12 -05:00
Mike Degatano
1b445feaaa Use full version in actions to prevent auto-updates (#3490) 2022-03-07 12:22:47 -05:00
dependabot[bot]
c05504a069 Bump actions/upload-artifact from 2.3.1 to 3 (#3484)
* Bump actions/upload-artifact from 2.3.1 to 3

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2.3.1 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2.3.1...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Use full version to prevent auto-updates

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-03-07 12:07:58 -05:00
Joakim Sørensen
e37cee9818 Update whoami URL (#3488) 2022-03-07 11:11:45 +01:00
Mike Degatano
dd3a4a1f47 Add unsupported message if no systemd-resolved (#3487)
* Require resolved for supported systems

* Added properties for dbus resolved
2022-03-07 10:38:24 +01:00
Pascal Vizeli
b451e555d3 Remove Debian buster from supported OS (#3483) 2022-03-03 18:23:19 +01:00
dependabot[bot]
5fb2b99917 Bump actions/checkout from 2.4.0 to 3 (#3478)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2.4.0 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.4.0...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2022-03-02 16:11:44 +01:00
Pascal Vizeli
8984d4afd6 Fix partial backup with Core (#3476)
* Fix partial backup with Core

* reformat

* same style
2022-03-02 15:54:34 +01:00
dependabot[bot]
7ae8dfe587 Bump docker/login-action from 1.14.0 to 1.14.1 (#3479)
Bumps [docker/login-action](https://github.com/docker/login-action) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1.14.0...v1.14.1)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-02 10:14:53 +01:00
Mike Degatano
c931a4c3e5 Error response handling & tests (#3473) 2022-03-01 21:52:50 +01:00
Mike Degatano
c58fa816d9 Use enum in arch to platform map (#3474) 2022-03-01 15:23:28 -05:00
dependabot[bot]
557f029aa0 Bump actions/setup-python from 2.3.2 to 3 (#3472)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.3.2 to 3.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2.3.2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-python
  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>
2022-03-01 09:39:34 +01:00
dependabot[bot]
e8e3cc2f67 Bump docker/login-action from 1.13.0 to 1.14.0 (#3471)
Bumps [docker/login-action](https://github.com/docker/login-action) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  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>
2022-03-01 09:39:24 +01:00
Mike Degatano
b0e4983488 Passing platform arg on image pull (#3465)
* Passing platform arg on image pull

* Passing in addon arch to image pull

* Move sys_arch above sys_plugins in setup

* Default to supervisor arch

* Cleanup from feedback
2022-03-01 09:38:58 +01:00
48 changed files with 1519 additions and 146 deletions

View File

@@ -31,6 +31,7 @@ categories:
- title: ":arrow_up: Dependency Updates"
label: "dependencies"
collapse-after: 1
include-labels:
- "breaking-change"

View File

@@ -50,7 +50,7 @@ jobs:
requirements: ${{ steps.requirements.outputs.changed }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
@@ -85,7 +85,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
@@ -110,14 +110,14 @@ jobs:
- name: Login to DockerHub
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v1.13.0
uses: docker/login-action@v1.14.1
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@v1.13.0
uses: docker/login-action@v1.14.1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -128,7 +128,7 @@ jobs:
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
- name: Build supervisor
uses: home-assistant/builder@2022.01.0
uses: home-assistant/builder@2022.03.1
with:
args: |
$BUILD_ARGS \
@@ -145,13 +145,13 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.publish == 'true'
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@@ -184,7 +184,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Initialize git
if: needs.init.outputs.publish == 'true'
@@ -209,11 +209,11 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Build the Supervisor
if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2022.01.0
uses: home-assistant/builder@2022.03.1
with:
args: |
--test \
@@ -278,6 +278,12 @@ jobs:
exit 1
fi
# Make sure it actually installed
test=$(docker exec hassio_cli ha addons info core_ssh --no-progress --raw-json | jq -r '.data.version')
if [[ "$test" == "null" ]]; then
exit 1
fi
echo "Start Core SSH Add-on"
test=$(docker exec hassio_cli ha addons start core_ssh --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ]; then
@@ -311,6 +317,58 @@ jobs:
exit 1
fi
- name: Create full backup
id: backup
run: |
test=$(docker exec hassio_cli ha backups new --no-progress --raw-json)
if [ "$(echo $test | jq -r '.result')" != "ok" ]; then
exit 1
fi
echo "::set-output name=slug::$(echo $test | jq -r '.data.slug')"
- name: Uninstall SSH add-on
run: |
test=$(docker exec hassio_cli ha addons uninstall core_ssh --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ]; then
exit 1
fi
- name: Restart supervisor
run: |
test=$(docker exec hassio_cli ha supervisor restart --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ]; then
exit 1
fi
- name: Wait for Supervisor to come up
run: |
SUPERVISOR=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' hassio_supervisor)
ping="error"
while [ "$ping" != "ok" ]; do
ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r '.result')
sleep 5
done
- name: Restore SSH add-on from backup
run: |
test=$(docker exec hassio_cli ha backups restore ${{ steps.backup.outputs.slug }} --addons core_ssh --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ]; then
exit 1
fi
# Make sure it actually installed
test=$(docker exec hassio_cli ha addons info core_ssh --no-progress --raw-json | jq -r '.data.version')
if [[ "$test" == "null" ]]; then
exit 1
fi
- name: Restore SSL directory from backup
run: |
test=$(docker exec hassio_cli ha backups restore ${{ steps.backup.outputs.slug }} --folders ssl --no-progress --raw-json | jq -r '.result')
if [ "$test" != "ok" ]; then
exit 1
fi
- name: Get supervisor logs on failiure
if: ${{ cancelled() || failure() }}
run: docker logs hassio_supervisor

View File

@@ -23,15 +23,15 @@ jobs:
name: Prepare Python ${{ matrix.python-version }} dependencies
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ matrix.python-version }}
id: python
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
with:
python-version: ${{ matrix.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -45,7 +45,7 @@ jobs:
pip install -r requirements.txt -r requirements_tests.txt
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -64,15 +64,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -93,7 +93,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Register hadolint problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
@@ -108,15 +108,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -128,7 +128,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -152,15 +152,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -184,15 +184,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -204,7 +204,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -225,15 +225,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -245,7 +245,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -269,15 +269,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -301,15 +301,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -321,7 +321,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -345,9 +345,9 @@ jobs:
name: Run tests Python ${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ matrix.python-version }}
@@ -357,7 +357,7 @@ jobs:
version: ${{ env.DEFAULT_CAS }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |
@@ -392,7 +392,7 @@ jobs:
-o console_output_style=count \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.0.0
with:
name: coverage-${{ matrix.python-version }}
path: .coverage
@@ -403,15 +403,15 @@ jobs:
needs: pytest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.1.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.1
with:
path: venv
key: |

View File

@@ -9,7 +9,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
- uses: dessant/lock-threads@v3.0.0
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@v2.4.0
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
@@ -36,7 +36,7 @@ jobs:
echo "::set-output name=version::$datepre.$newpost"
- name: Run Release Drafter
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v5.19.0
with:
tag: ${{ steps.version.outputs.version }}
name: ${{ steps.version.outputs.version }}

View File

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

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
- uses: actions/stale@v5.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60

View File

@@ -2,23 +2,23 @@ aiohttp==3.8.1
async_timeout==4.0.2
atomicwrites==1.4.0
attrs==21.4.0
awesomeversion==22.2.0
awesomeversion==22.4.0
brotli==1.0.9
cchardet==2.1.7
ciso8601==2.2.0
colorlog==6.6.0
cpe==1.2.1
cryptography==36.0.1
debugpy==1.5.1
cryptography==36.0.2
debugpy==1.6.0
deepmerge==1.0.1
dirhash==0.2.1
docker==5.0.3
gitpython==3.1.27
jinja2==3.0.3
pulsectl==22.1.3
jinja2==3.1.1
pulsectl==22.3.2
pyudev==0.23.2
ruamel.yaml==0.17.17
securetar==2022.2.0
sentry-sdk==1.5.6
voluptuous==0.12.2
sentry-sdk==1.5.8
voluptuous==0.13.0
dbus-next==0.2.3

View File

@@ -1,14 +1,14 @@
black==22.1.0
black==22.3.0
codecov==2.1.12
coverage==6.3.2
flake8-docstrings==1.6.0
flake8==4.0.1
pre-commit==2.17.0
pre-commit==2.18.1
pydocstyle==6.1.1
pylint==2.12.2
pylint==2.13.4
pytest-aiohttp==0.3.0
pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16)
pytest-cov==3.0.0
pytest-timeout==2.1.0
pytest==7.0.1
pyupgrade==2.31.0
pytest==7.1.1
pyupgrade==2.31.1

View File

@@ -178,7 +178,7 @@ class AddonManager(CoreSysAttributes):
await addon.install_apparmor()
try:
await addon.instance.install(store.version, store.image)
await addon.instance.install(store.version, store.image, arch=addon.arch)
except DockerError as err:
self.data.uninstall(addon)
raise AddonsError() from err

View File

@@ -503,6 +503,14 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return list of supported machine."""
return self.data.get(ATTR_MACHINE, [])
@property
def arch(self) -> str:
"""Return architecture to use for the addon's image."""
if ATTR_IMAGE in self.data:
return self.sys_arch.match(self.data[ATTR_ARCH])
return self.sys_arch.default
@property
def image(self) -> Optional[str]:
"""Generate image name from data."""

View File

@@ -9,7 +9,7 @@ from aiohttp import web
from aiohttp.hdrs import CONTENT_DISPOSITION
import voluptuous as vol
from ..backups.validate import ALL_FOLDERS
from ..backups.validate import ALL_FOLDERS, FOLDER_HOMEASSISTANT
from ..const import (
ATTR_ADDONS,
ATTR_BACKUPS,
@@ -36,13 +36,16 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+")
# Backwards compatible / Remove 2022.08
_ALL_FOLDERS = ALL_FOLDERS + [FOLDER_HOMEASSISTANT]
# pylint: disable=no-value-for-parameter
SCHEMA_RESTORE_PARTIAL = vol.Schema(
{
vol.Optional(ATTR_PASSWORD): vol.Maybe(str),
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
vol.Optional(ATTR_ADDONS): vol.All([str], vol.Unique()),
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(ALL_FOLDERS)], vol.Unique()),
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(_ALL_FOLDERS)], vol.Unique()),
}
)
@@ -59,7 +62,7 @@ SCHEMA_BACKUP_FULL = vol.Schema(
SCHEMA_BACKUP_PARTIAL = SCHEMA_BACKUP_FULL.extend(
{
vol.Optional(ATTR_ADDONS): vol.All([str], vol.Unique()),
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(ALL_FOLDERS)], vol.Unique()),
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(_ALL_FOLDERS)], vol.Unique()),
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
}
)

View File

@@ -444,7 +444,7 @@ class Backup(CoreSysAttributes):
except (tarfile.TarError, OSError) as err:
_LOGGER.warning("Can't restore folder %s: %s", name, err)
await self.sys_run_in_executor(_restore, name)
await self.sys_run_in_executor(_restore)
# Restore folder sequential
# avoid issue on slow IO

View File

@@ -144,13 +144,11 @@ class BackupManager(CoreSysAttributes):
_LOGGER.info("Backing up %s store Add-ons", backup.slug)
await backup.store_addons(addon_list)
# Backup folders
# HomeAssistant Folder is for v1
if homeassistant or FOLDER_HOMEASSISTANT in folder_list:
if homeassistant:
await backup.store_homeassistant()
folder_list = list(folder_list)
folder_list.remove(FOLDER_HOMEASSISTANT)
# Backup folders
if folder_list:
_LOGGER.info("Backing up %s store folders", backup.slug)
await backup.store_folders(folder_list)
@@ -186,12 +184,12 @@ class BackupManager(CoreSysAttributes):
@Job(conditions=[JobCondition.FREE_SPACE, JobCondition.RUNNING])
async def do_backup_partial(
self,
name="",
addons=None,
folders=None,
password=None,
homeassistant=True,
compressed=True,
name: str = "",
addons: list[str] | None = None,
folders: list[str] | None = None,
password: str | None = None,
homeassistant: bool = False,
compressed: bool = True,
):
"""Create a partial backup."""
if self.lock.locked():
@@ -201,6 +199,11 @@ class BackupManager(CoreSysAttributes):
addons = addons or []
folders = folders or []
# HomeAssistant Folder is for v1
if FOLDER_HOMEASSISTANT in folders:
folders.remove(FOLDER_HOMEASSISTANT)
homeassistant = True
if len(addons) == 0 and len(folders) == 0 and not homeassistant:
_LOGGER.error("Nothing to create backup for")
@@ -227,15 +230,10 @@ class BackupManager(CoreSysAttributes):
self,
backup: Backup,
addon_list: list[str],
folder_list: list[Path],
folder_list: list[str],
homeassistant: bool,
replace: bool,
):
# Version 1
if FOLDER_HOMEASSISTANT in folder_list:
folder_list.remove(FOLDER_HOMEASSISTANT)
homeassistant = backup.homeassistant_version is not None
try:
task_hass: asyncio.Task | None = None
async with backup:
@@ -356,6 +354,14 @@ class BackupManager(CoreSysAttributes):
_LOGGER.error("A backup/restore process is already running")
return False
addon_list = addons or []
folder_list = folders or []
# Version 1
if FOLDER_HOMEASSISTANT in folder_list:
folder_list.remove(FOLDER_HOMEASSISTANT)
homeassistant = True
if backup.protected and not backup.set_password(password):
_LOGGER.error("Invalid password for backup %s", backup.slug)
return False
@@ -364,9 +370,6 @@ class BackupManager(CoreSysAttributes):
_LOGGER.error("No Home Assistant Core data inside the backup")
return False
addon_list = addons or []
folder_list = folders or []
_LOGGER.info("Partial-Restore %s start", backup.slug)
async with self.lock:
self.sys_core.state = CoreState.FREEZE

View File

@@ -31,7 +31,6 @@ from ..const import (
from ..validate import SCHEMA_DOCKER_CONFIG, repositories, version_tag
ALL_FOLDERS = [
FOLDER_HOMEASSISTANT,
FOLDER_SHARE,
FOLDER_ADDONS,
FOLDER_SSL,
@@ -68,6 +67,13 @@ def v1_folderlist(folder_data: list[str]) -> list[str]:
return folder_data
def v1_protected(protected: bool | str) -> bool:
"""Cleanup old protected handling."""
if isinstance(protected, bool):
return protected
return True
# pylint: disable=no-value-for-parameter
SCHEMA_BACKUP = vol.Schema(
{
@@ -77,7 +83,9 @@ SCHEMA_BACKUP = vol.Schema(
vol.Required(ATTR_NAME): str,
vol.Required(ATTR_DATE): str,
vol.Optional(ATTR_COMPRESSED, default=True): vol.Boolean(),
vol.Optional(ATTR_PROTECTED, default=False): vol.Boolean(),
vol.Optional(ATTR_PROTECTED, default=False): vol.All(
v1_protected, vol.Boolean()
),
vol.Optional(ATTR_CRYPTO, default=None): vol.Maybe(CRYPTO_AES128),
vol.Optional(ATTR_HOMEASSISTANT, default=None): vol.All(
v1_homeassistant,

View File

@@ -454,3 +454,13 @@ class BusEvent(str, Enum):
HARDWARE_NEW_DEVICE = "hardware_new_device"
HARDWARE_REMOVE_DEVICE = "hardware_remove_device"
class CpuArch(str, Enum):
"""Supported CPU architectures."""
ARMV7 = "armv7"
ARMHF = "armhf"
AARCH64 = "aarch64"
I386 = "i386"
AMD64 = "amd64"

View File

@@ -1,11 +1,13 @@
"""Constants for DBUS."""
from enum import Enum
from enum import Enum, IntEnum
from socket import AF_INET, AF_INET6
DBUS_NAME_HAOS = "io.hass.os"
DBUS_NAME_HOSTNAME = "org.freedesktop.hostname1"
DBUS_NAME_LOGIND = "org.freedesktop.login1"
DBUS_NAME_NM = "org.freedesktop.NetworkManager"
DBUS_NAME_RAUC = "de.pengutronix.rauc"
DBUS_NAME_RESOLVED = "org.freedesktop.resolve1"
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
DBUS_NAME_TIMEDATE = "org.freedesktop.timedate1"
@@ -24,6 +26,7 @@ DBUS_IFACE_IP4CONFIG = "org.freedesktop.NetworkManager.IP4Config"
DBUS_IFACE_IP6CONFIG = "org.freedesktop.NetworkManager.IP6Config"
DBUS_IFACE_NM = "org.freedesktop.NetworkManager"
DBUS_IFACE_RAUC_INSTALLER = "de.pengutronix.rauc.Installer"
DBUS_IFACE_RESOLVED_MANAGER = "org.freedesktop.resolve1.Manager"
DBUS_IFACE_SETTINGS_CONNECTION = "org.freedesktop.NetworkManager.Settings.Connection"
DBUS_IFACE_SYSTEMD_MANAGER = "org.freedesktop.systemd1.Manager"
DBUS_IFACE_TIMEDATE = "org.freedesktop.timedate1"
@@ -43,6 +46,7 @@ DBUS_OBJECT_HAOS_SYSTEM = "/io/hass/os/System"
DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1"
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
DBUS_OBJECT_RESOLVED = "/org/freedesktop/resolve1"
DBUS_OBJECT_SETTINGS = "/org/freedesktop/NetworkManager/Settings"
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
@@ -52,19 +56,33 @@ DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
DBUS_ATTR_ADDRESS_DATA = "AddressData"
DBUS_ATTR_BOOT_SLOT = "BootSlot"
DBUS_ATTR_CACHE_STATISTICS = "CacheStatistics"
DBUS_ATTR_CHASSIS = "Chassis"
DBUS_ATTR_COMPATIBLE = "Compatible"
DBUS_ATTR_CONFIGURATION = "Configuration"
DBUS_ATTR_CONNECTION = "Connection"
DBUS_ATTR_CONNECTION_ENABLED = "ConnectivityCheckEnabled"
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
DBUS_ATTR_CURRENT_DNS_SERVER = "CurrentDNSServer"
DBUS_ATTR_CURRENT_DNS_SERVER_EX = "CurrentDNSServerEx"
DBUS_ATTR_DEFAULT = "Default"
DBUS_ATTR_DEPLOYMENT = "Deployment"
DBUS_ATTR_DEVICE_INTERFACE = "Interface"
DBUS_ATTR_DEVICE_TYPE = "DeviceType"
DBUS_ATTR_DEVICES = "Devices"
DBUS_ATTR_DIAGNOSTICS = "Diagnostics"
DBUS_ATTR_DNS = "DNS"
DBUS_ATTR_DNS_EX = "DNSEx"
DBUS_ATTR_DNS_OVER_TLS = "DNSOverTLS"
DBUS_ATTR_DNS_STUB_LISTENER = "DNSStubListener"
DBUS_ATTR_DNSSEC = "DNSSEC"
DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS = "DNSSECNegativeTrustAnchors"
DBUS_ATTR_DNSSEC_STATISTICS = "DNSSECStatistics"
DBUS_ATTR_DNSSEC_SUPPORTED = "DNSSECSupported"
DBUS_ATTR_DOMAINS = "Domains"
DBUS_ATTR_DRIVER = "Driver"
DBUS_ATTR_FALLBACK_DNS = "FallbackDNS"
DBUS_ATTR_FALLBACK_DNS_EX = "FallbackDNSEx"
DBUS_ATTR_FINISH_TIMESTAMP = "FinishTimestamp"
DBUS_ATTR_FIRMWARE_TIMESTAMP_MONOTONIC = "FirmwareTimestampMonotonic"
DBUS_ATTR_FREQUENCY = "Frequency"
@@ -76,11 +94,13 @@ DBUS_ATTR_IP6CONFIG = "Ip6Config"
DBUS_ATTR_KERNEL_RELEASE = "KernelRelease"
DBUS_ATTR_KERNEL_TIMESTAMP_MONOTONIC = "KernelTimestampMonotonic"
DBUS_ATTR_LAST_ERROR = "LastError"
DBUS_ATTR_LLMNR = "LLMNR"
DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname"
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
DBUS_ATTR_LOCALRTC = "LocalRTC"
DBUS_ATTR_MANAGED = "Managed"
DBUS_ATTR_MODE = "Mode"
DBUS_ATTR_MODE = "Mode"
DBUS_ATTR_MULTICAST_DNS = "MulticastDNS"
DBUS_ATTR_NAMESERVER_DATA = "NameserverData"
DBUS_ATTR_NAMESERVERS = "Nameservers"
DBUS_ATTR_NTP = "NTP"
@@ -89,6 +109,7 @@ DBUS_ATTR_OPERATING_SYSTEM_PRETTY_NAME = "OperatingSystemPrettyName"
DBUS_ATTR_OPERATION = "Operation"
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
DBUS_ATTR_PRIMARY_CONNECTION = "PrimaryConnection"
DBUS_ATTR_RESOLV_CONF_MODE = "ResolvConfMode"
DBUS_ATTR_RCMANAGER = "RcManager"
DBUS_ATTR_SSID = "Ssid"
DBUS_ATTR_STATE = "State"
@@ -97,6 +118,7 @@ DBUS_ATTR_STATIC_OPERATING_SYSTEM_CPE_NAME = "OperatingSystemCPEName"
DBUS_ATTR_STRENGTH = "Strength"
DBUS_ATTR_TIMEUSEC = "TimeUSec"
DBUS_ATTR_TIMEZONE = "Timezone"
DBUS_ATTR_TRANSACTION_STATISTICS = "TransactionStatistics"
DBUS_ATTR_TYPE = "Type"
DBUS_ATTR_USERSPACE_TIMESTAMP_MONOTONIC = "UserspaceTimestampMonotonic"
DBUS_ATTR_UUID = "Uuid"
@@ -176,3 +198,53 @@ class WirelessMethodType(int, Enum):
INFRASTRUCTURE = 2
ACCESSPOINT = 3
MESH = 4
class DNSAddressFamily(IntEnum):
"""Address family for DNS server."""
INET = AF_INET
INET6 = AF_INET6
class MulticastProtocolEnabled(str, Enum):
"""Multicast protocol enabled or resolve."""
YES = "yes"
NO = "no"
RESOLVE = "resolve"
class DNSOverTLSEnabled(str, Enum):
"""DNS over TLS enabled."""
YES = "yes"
NO = "no"
OPPORTUNISTIC = "opportunistic"
class DNSSECValidation(str, Enum):
"""DNSSEC validation enforced."""
YES = "yes"
NO = "no"
ALLOW_DOWNGRADE = "allow-downgrade"
class DNSStubListenerEnabled(str, Enum):
"""DNS stub listener enabled."""
YES = "yes"
NO = "no"
TCP_ONLY = "tcp"
UDP_ONLY = "udp"
class ResolvConfMode(str, Enum):
"""Resolv.conf management mode."""
FOREIGN = "foreign"
MISSING = "missing"
STATIC = "static"
STUB = "stub"
UPLINK = "uplink"

View File

@@ -9,6 +9,7 @@ from .interface import DBusInterface
from .logind import Logind
from .network import NetworkManager
from .rauc import Rauc
from .resolved import Resolved
from .systemd import Systemd
from .timedate import TimeDate
@@ -29,6 +30,7 @@ class DBusManager(CoreSysAttributes):
self._network: NetworkManager = NetworkManager()
self._agent: OSAgent = OSAgent()
self._timedate: TimeDate = TimeDate()
self._resolved: Resolved = Resolved()
@property
def systemd(self) -> Systemd:
@@ -65,6 +67,11 @@ class DBusManager(CoreSysAttributes):
"""Return the timedate interface."""
return self._timedate
@property
def resolved(self) -> Resolved:
"""Return the resolved interface."""
return self._resolved
async def load(self) -> None:
"""Connect interfaces to D-Bus."""
if not SOCKET_DBUS.exists():
@@ -81,6 +88,7 @@ class DBusManager(CoreSysAttributes):
self.timedate,
self.network,
self.rauc,
self.resolved,
]
for dbus in dbus_loads:
_LOGGER.info("Load dbus interface %s", dbus.name)

188
supervisor/dbus/resolved.py Normal file
View File

@@ -0,0 +1,188 @@
"""D-Bus interface for systemd-resolved."""
from __future__ import annotations
import logging
from typing import Any
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus
from .const import (
DBUS_ATTR_CACHE_STATISTICS,
DBUS_ATTR_CURRENT_DNS_SERVER,
DBUS_ATTR_CURRENT_DNS_SERVER_EX,
DBUS_ATTR_DNS,
DBUS_ATTR_DNS_EX,
DBUS_ATTR_DNS_OVER_TLS,
DBUS_ATTR_DNS_STUB_LISTENER,
DBUS_ATTR_DNSSEC,
DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS,
DBUS_ATTR_DNSSEC_STATISTICS,
DBUS_ATTR_DNSSEC_SUPPORTED,
DBUS_ATTR_DOMAINS,
DBUS_ATTR_FALLBACK_DNS,
DBUS_ATTR_FALLBACK_DNS_EX,
DBUS_ATTR_LLMNR,
DBUS_ATTR_LLMNR_HOSTNAME,
DBUS_ATTR_MULTICAST_DNS,
DBUS_ATTR_RESOLV_CONF_MODE,
DBUS_ATTR_TRANSACTION_STATISTICS,
DBUS_IFACE_RESOLVED_MANAGER,
DBUS_NAME_RESOLVED,
DBUS_OBJECT_RESOLVED,
DNSAddressFamily,
DNSOverTLSEnabled,
DNSSECValidation,
DNSStubListenerEnabled,
MulticastProtocolEnabled,
ResolvConfMode,
)
from .interface import DBusInterface, dbus_property
from .utils import dbus_connected
_LOGGER: logging.Logger = logging.getLogger(__name__)
class Resolved(DBusInterface):
"""Handle D-Bus interface for systemd-resolved."""
name = DBUS_NAME_RESOLVED
def __init__(self):
"""Initialize Properties."""
self.properties: dict[str, Any] = {}
async def connect(self):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_RESOLVED, DBUS_OBJECT_RESOLVED)
except DBusError:
_LOGGER.warning("Can't connect to systemd-resolved.")
except DBusInterfaceError:
_LOGGER.warning(
"Host has no systemd-resolved support. DNS will not work correctly."
)
@property
@dbus_property
def cache_statistics(self) -> tuple[int, int, int] | None:
"""Return current cache entries and hits and misses since last reset."""
return self.properties[DBUS_ATTR_CACHE_STATISTICS]
@property
@dbus_property
def current_dns_server(
self,
) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
"""Return current DNS server."""
return self.properties[DBUS_ATTR_CURRENT_DNS_SERVER]
@property
@dbus_property
def current_dns_server_ex(
self,
) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
"""Return current DNS server including port and server name."""
return self.properties[DBUS_ATTR_CURRENT_DNS_SERVER_EX]
@property
@dbus_property
def dns(self) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
"""Return DNS servers in use."""
return self.properties[DBUS_ATTR_DNS]
@property
@dbus_property
def dns_ex(self) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
"""Return DNS servers in use including port and server name."""
return self.properties[DBUS_ATTR_DNS_EX]
@property
@dbus_property
def dns_over_tls(self) -> DNSOverTLSEnabled | None:
"""Return DNS over TLS enabled."""
return self.properties[DBUS_ATTR_DNS_OVER_TLS]
@property
@dbus_property
def dns_stub_listener(self) -> DNSStubListenerEnabled | None:
"""Return DNS stub listener enabled on port 53."""
return self.properties[DBUS_ATTR_DNS_STUB_LISTENER]
@property
@dbus_property
def dnssec(self) -> DNSSECValidation | None:
"""Return DNSSEC validation enforced."""
return self.properties[DBUS_ATTR_DNSSEC]
@property
@dbus_property
def dnssec_negative_trust_anchors(self) -> list[str] | None:
"""Return DNSSEC negative trust anchors."""
return self.properties[DBUS_ATTR_DNSSEC_NEGATIVE_TRUST_ANCHORS]
@property
@dbus_property
def dnssec_statistics(self) -> tuple[int, int, int, int] | None:
"""Return Secure, insecure, bogus, and indeterminate DNSSEC validations since last reset."""
return self.properties[DBUS_ATTR_DNSSEC_STATISTICS]
@property
@dbus_property
def dnssec_supported(self) -> bool | None:
"""Return DNSSEC enabled and selected DNS servers support it."""
return self.properties[DBUS_ATTR_DNSSEC_SUPPORTED]
@property
@dbus_property
def domains(self) -> list[tuple[int, str, bool]] | None:
"""Return search and routing domains in use."""
return self.properties[DBUS_ATTR_DOMAINS]
@property
@dbus_property
def fallback_dns(self) -> list[tuple[int, DNSAddressFamily, bytes]] | None:
"""Return fallback DNS servers."""
return self.properties[DBUS_ATTR_FALLBACK_DNS]
@property
@dbus_property
def fallback_dns_ex(
self,
) -> list[tuple[int, DNSAddressFamily, bytes, int, str]] | None:
"""Return fallback DNS servers including port and server name."""
return self.properties[DBUS_ATTR_FALLBACK_DNS_EX]
@property
@dbus_property
def llmnr(self) -> MulticastProtocolEnabled | None:
"""Return LLMNR enabled."""
return self.properties[DBUS_ATTR_LLMNR]
@property
@dbus_property
def llmnr_hostname(self) -> str | None:
"""Return LLMNR hostname on network."""
return self.properties[DBUS_ATTR_LLMNR_HOSTNAME]
@property
@dbus_property
def multicast_dns(self) -> MulticastProtocolEnabled | None:
"""Return MDNS enabled."""
return self.properties[DBUS_ATTR_MULTICAST_DNS]
@property
@dbus_property
def resolv_conf_mode(self) -> ResolvConfMode | None:
"""Return how /etc/resolv.conf managed on host."""
return self.properties[DBUS_ATTR_RESOLV_CONF_MODE]
@property
@dbus_property
def transaction_statistics(self) -> tuple[int, int] | None:
"""Return transactions processing and processed since last reset."""
return self.properties[DBUS_ATTR_TRANSACTION_STATISTICS]
@dbus_connected
async def update(self):
"""Update Properties."""
self.properties = await self.dbus.get_properties(DBUS_IFACE_RESOLVED_MANAGER)

View File

@@ -31,6 +31,7 @@ from ..const import (
SYSTEMD_JOURNAL_PERSISTENT,
SYSTEMD_JOURNAL_VOLATILE,
BusEvent,
CpuArch,
)
from ..coresys import CoreSys
from ..exceptions import (
@@ -515,7 +516,11 @@ class DockerAddon(DockerInterface):
)
def _install(
self, version: AwesomeVersion, image: str | None = None, latest: bool = False
self,
version: AwesomeVersion,
image: str | None = None,
latest: bool = False,
arch: CpuArch | None = None,
) -> None:
"""Pull Docker image or build it.
@@ -524,7 +529,7 @@ class DockerAddon(DockerInterface):
if self.addon.need_build:
self._build(version)
else:
super()._install(version, image, latest)
super()._install(version, image, latest, arch)
def _build(self, version: AwesomeVersion) -> None:
"""Build a Docker container.
@@ -682,7 +687,7 @@ class DockerAddon(DockerInterface):
self.sys_security.verify_content(self.addon.codenotary, checksum),
self.sys_loop,
)
job.result(timeout=20)
job.result()
@Job(conditions=[JobCondition.OS_AGENT])
async def _hardware_events(self, device: Device) -> None:

View File

@@ -1,9 +1,11 @@
"""Interface class for Supervisor Docker object."""
from __future__ import annotations
import asyncio
from contextlib import suppress
import logging
import re
from typing import Any, Awaitable, Optional
from typing import Any, Awaitable
from awesomeversion import AwesomeVersion
from awesomeversion.strategy import AwesomeVersionStrategy
@@ -17,6 +19,7 @@ from ..const import (
ATTR_USERNAME,
LABEL_ARCH,
LABEL_VERSION,
CpuArch,
)
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import (
@@ -37,6 +40,14 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
IMAGE_WITH_HOST = re.compile(r"^((?:[a-z0-9]+(?:-[a-z0-9]+)*\.)+[a-z]{2,})\/.+")
DOCKER_HUB = "hub.docker.com"
MAP_ARCH = {
CpuArch.ARMV7: "linux/arm/v7",
CpuArch.ARMHF: "linux/arm/v6",
CpuArch.AARCH64: "linux/arm64",
CpuArch.I386: "linux/386",
CpuArch.AMD64: "linux/amd64",
}
class DockerInterface(CoreSysAttributes):
"""Docker Supervisor interface."""
@@ -44,7 +55,7 @@ class DockerInterface(CoreSysAttributes):
def __init__(self, coresys: CoreSys):
"""Initialize Docker base wrapper."""
self.coresys: CoreSys = coresys
self._meta: Optional[dict[str, Any]] = None
self._meta: dict[str, Any] | None = None
self.lock: asyncio.Lock = asyncio.Lock()
@property
@@ -53,7 +64,7 @@ class DockerInterface(CoreSysAttributes):
return 10
@property
def name(self) -> Optional[str]:
def name(self) -> str | None:
"""Return name of Docker container."""
return None
@@ -77,7 +88,7 @@ class DockerInterface(CoreSysAttributes):
return self.meta_config.get("Labels") or {}
@property
def image(self) -> Optional[str]:
def image(self) -> str | None:
"""Return name of Docker image."""
try:
return self.meta_config["Image"].partition(":")[0]
@@ -85,14 +96,14 @@ class DockerInterface(CoreSysAttributes):
return None
@property
def version(self) -> Optional[AwesomeVersion]:
def version(self) -> AwesomeVersion | None:
"""Return version of Docker image."""
if LABEL_VERSION not in self.meta_labels:
return None
return AwesomeVersion(self.meta_labels[LABEL_VERSION])
@property
def arch(self) -> Optional[str]:
def arch(self) -> str | None:
"""Return arch of Docker image."""
return self.meta_labels.get(LABEL_ARCH)
@@ -150,19 +161,28 @@ class DockerInterface(CoreSysAttributes):
@process_lock
def install(
self, version: AwesomeVersion, image: Optional[str] = None, latest: bool = False
self,
version: AwesomeVersion,
image: str | None = None,
latest: bool = False,
arch: CpuArch | None = None,
):
"""Pull docker image."""
return self.sys_run_in_executor(self._install, version, image, latest)
return self.sys_run_in_executor(self._install, version, image, latest, arch)
def _install(
self, version: AwesomeVersion, image: Optional[str] = None, latest: bool = False
self,
version: AwesomeVersion,
image: str | None = None,
latest: bool = False,
arch: CpuArch | None = None,
) -> None:
"""Pull Docker image.
Need run inside executor.
"""
image = image or self.image
arch = arch or self.sys_arch.supervisor
_LOGGER.info("Downloading docker image %s with tag %s.", image, version)
try:
@@ -171,7 +191,10 @@ class DockerInterface(CoreSysAttributes):
self._docker_login(image)
# Pull new image
docker_image = self.sys_docker.images.pull(f"{image}:{version!s}")
docker_image = self.sys_docker.images.pull(
f"{image}:{version!s}",
platform=MAP_ARCH[arch],
)
# Validate content
try:
@@ -378,13 +401,13 @@ class DockerInterface(CoreSysAttributes):
@process_lock
def update(
self, version: AwesomeVersion, image: Optional[str] = None, latest: bool = False
self, version: AwesomeVersion, image: str | None = None, latest: bool = False
) -> Awaitable[None]:
"""Update a Docker image."""
return self.sys_run_in_executor(self._update, version, image, latest)
def _update(
self, version: AwesomeVersion, image: Optional[str] = None, latest: bool = False
self, version: AwesomeVersion, image: str | None = None, latest: bool = False
) -> None:
"""Update a docker image.
@@ -428,11 +451,11 @@ class DockerInterface(CoreSysAttributes):
return b""
@process_lock
def cleanup(self, old_image: Optional[str] = None) -> Awaitable[None]:
def cleanup(self, old_image: str | None = None) -> Awaitable[None]:
"""Check if old version exists and cleanup."""
return self.sys_run_in_executor(self._cleanup, old_image)
def _cleanup(self, old_image: Optional[str] = None) -> None:
def _cleanup(self, old_image: str | None = None) -> None:
"""Check if old version exists and cleanup.
Need run inside executor.
@@ -635,7 +658,7 @@ class DockerInterface(CoreSysAttributes):
job = asyncio.run_coroutine_threadsafe(
self.sys_security.verify_own_content(checksum), self.sys_loop
)
job.result(timeout=20)
job.result()
@process_lock
def check_trust(self) -> Awaitable[None]:

View File

@@ -68,7 +68,7 @@ class HostManager(CoreSysAttributes):
"""Return a list of host features."""
return self.supported_features()
@lru_cache
@lru_cache(maxsize=128)
def supported_features(self) -> list[HostFeature]:
"""Return a list of supported host features."""
features = []
@@ -95,21 +95,29 @@ class HostManager(CoreSysAttributes):
return features
async def reload(self):
async def reload(
self,
*,
services: bool = True,
network: bool = True,
agent: bool = True,
audio: bool = True,
):
"""Reload host functions."""
await self.info.update()
if self.sys_dbus.systemd.is_connected:
if services and self.sys_dbus.systemd.is_connected:
await self.services.update()
if self.sys_dbus.network.is_connected:
if network and self.sys_dbus.network.is_connected:
await self.network.update()
if self.sys_dbus.agent.is_connected:
if agent and self.sys_dbus.agent.is_connected:
await self.sys_dbus.agent.update()
with suppress(PulseAudioError):
await self.sound.update()
if audio:
with suppress(PulseAudioError):
await self.sound.update()
_LOGGER.info("Host information reload completed")
self.supported_features.cache_clear() # pylint: disable=no-member
@@ -117,7 +125,8 @@ class HostManager(CoreSysAttributes):
async def load(self):
"""Load host information."""
with suppress(HassioError):
await self.reload()
await self.reload(network=False)
await self.network.load()
# Register for events
self.sys_bus.register_event(BusEvent.HARDWARE_NEW_DEVICE, self._hardware_events)

View File

@@ -2,11 +2,15 @@
from __future__ import annotations
import asyncio
from contextlib import suppress
from ipaddress import IPv4Address, IPv4Interface, IPv6Address, IPv6Interface
import logging
import attr
from supervisor.jobs.const import JobCondition
from supervisor.jobs.decorator import Job
from ..const import ATTR_HOST_INTERNET
from ..coresys import CoreSys, CoreSysAttributes
from ..dbus.const import (
@@ -79,7 +83,6 @@ class NetworkManager(CoreSysAttributes):
async def check_connectivity(self):
"""Check the internet connection."""
if not self.sys_dbus.network.connectivity_enabled:
return
@@ -100,6 +103,25 @@ class NetworkManager(CoreSysAttributes):
self.sys_dbus.network.interfaces[inet_name]
)
@Job(conditions=JobCondition.HOST_NETWORK)
async def load(self):
"""Load network information and reapply defaults over dbus."""
await self.update()
# Apply current settings on each interface so OS can update any out of date defaults
interfaces = [
Interface.from_dbus_interface(self.sys_dbus.network.interfaces[i])
for i in self.sys_dbus.network.interfaces
]
with suppress(HostNetworkNotFound):
await asyncio.gather(
*[
self.apply_changes(interface, update_only=True)
for interface in interfaces
if interface.enabled
]
)
async def update(self):
"""Update properties over dbus."""
_LOGGER.info("Updating local network information")
@@ -114,7 +136,9 @@ class NetworkManager(CoreSysAttributes):
await self.check_connectivity()
async def apply_changes(self, interface: Interface) -> None:
async def apply_changes(
self, interface: Interface, *, update_only: bool = False
) -> None:
"""Apply Interface changes to host."""
inet = self.sys_dbus.network.interfaces.get(interface.name)
con: NetworkConnection = None
@@ -147,6 +171,13 @@ class NetworkManager(CoreSysAttributes):
f"Can't update config on {interface.name}: {err}", _LOGGER.error
) from err
# Stop if only updates are allowed as other paths create/delete interfaces
elif update_only:
raise HostNetworkNotFound(
f"Requested to update interface {interface.name} which does not exist or is disabled.",
_LOGGER.warning,
)
# Create new configuration and activate interface
elif inet and interface.enabled:
_LOGGER.debug("Create new configuration for %s", interface.name)

View File

@@ -19,6 +19,7 @@ class JobCondition(str, Enum):
RUNNING = "running"
HAOS = "haos"
OS_AGENT = "os_agent"
HOST_NETWORK = "host_network"
class JobExecutionLimit(str, Enum):

View File

@@ -181,6 +181,14 @@ class Job(CoreSysAttributes):
f"'{self._method.__qualname__}' blocked from execution, no Home Assistant OS-Agent available"
)
if (
JobCondition.HOST_NETWORK in self.conditions
and not self.sys_dbus.network.is_connected
):
raise JobConditionException(
f"'{self._method.__qualname__}' blocked from execution, host Network Manager not available"
)
async def _acquire_exection_limit(self) -> None:
"""Process exection limits."""
if self.limit not in (

View File

@@ -56,4 +56,4 @@ class CheckCoreTrust(CheckBase):
@property
def states(self) -> list[CoreState]:
"""Return a list of valid states when this check can run."""
return [CoreState.RUNNING, CoreState.STARTUP]
return [CoreState.RUNNING]

View File

@@ -62,4 +62,4 @@ class CheckPluginTrust(CheckBase):
@property
def states(self) -> list[CoreState]:
"""Return a list of valid states when this check can run."""
return [CoreState.RUNNING, CoreState.STARTUP]
return [CoreState.RUNNING]

View File

@@ -41,6 +41,7 @@ class UnsupportedReason(str, Enum):
SOFTWARE = "software"
SOURCE_MODS = "source_mods"
SYSTEMD = "systemd"
SYSTEMD_RESOLVED = "systemd_resolved"
class UnhealthyReason(str, Enum):

View File

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

View File

@@ -0,0 +1,34 @@
"""Evaluation class for systemd-resolved."""
from ...const import CoreState
from ...coresys import CoreSys
from ..const import UnsupportedReason
from .base import EvaluateBase
def setup(coresys: CoreSys) -> EvaluateBase:
"""Initialize evaluation-setup function."""
return EvaluateResolved(coresys)
class EvaluateResolved(EvaluateBase):
"""Evaluate systemd-resolved."""
@property
def reason(self) -> UnsupportedReason:
"""Return a UnsupportedReason enum."""
return UnsupportedReason.SYSTEMD_RESOLVED
@property
def on_failure(self) -> str:
"""Return a string that is printed when self.evaluate is False."""
return "Systemd-Resolved is required for DNS in Home Assistant."
@property
def states(self) -> list[CoreState]:
"""Return a list of valid states when this evaluation can run."""
return [CoreState.SETUP]
async def evaluate(self) -> bool:
"""Run evaluation."""
return not self.sys_dbus.resolved.is_connected

View File

@@ -1,6 +1,5 @@
"""Fetch last versions from webserver."""
import logging
from typing import Awaitable
from .const import (
ATTR_CONTENT_TRUST,
@@ -71,9 +70,11 @@ class Security(FileConfiguration, CoreSysAttributes):
raise
return
def verify_own_content(self, checksum: str) -> Awaitable[None]:
async def verify_own_content(self, checksum: str) -> None:
"""Verify content from HA org."""
return self.verify_content("notary@home-assistant.io", checksum)
return
# pylint: disable=unreachable
return await self.verify_content("notary@home-assistant.io", checksum)
async def verify_secret(self, pwned_hash: str) -> None:
"""Verify pwned state of a secret."""

View File

@@ -1,11 +1,13 @@
"""Small wrapper for CodeNotary."""
from __future__ import annotations
import asyncio
import hashlib
import json
import logging
from pathlib import Path
import shlex
from typing import Final, Union
from typing import Final
import async_timeout
from dirhash import dirhash
@@ -21,10 +23,11 @@ _CAS_CMD: str = (
_CACHE: set[tuple[str, str]] = set()
_ATTR_ERROR: Final = "error"
_ATTR_STATUS: Final = "status"
def calc_checksum(data: Union[str, bytes]) -> str:
def calc_checksum(data: str | bytes) -> str:
"""Generate checksum for CodeNotary."""
if isinstance(data, str):
return hashlib.sha256(data.encode()).hexdigest()
@@ -58,15 +61,15 @@ async def cas_validate(
env=clean_env(),
)
async with async_timeout.timeout(30):
async with async_timeout.timeout(15):
data, error = await proc.communicate()
except OSError as err:
raise CodeNotaryError(
f"CodeNotary fatal error: {err!s}", _LOGGER.critical
) from err
except asyncio.TimeoutError:
raise CodeNotaryError(
"Timeout while processing CodeNotary", _LOGGER.error
raise CodeNotaryBackendError(
"Timeout while processing CodeNotary", _LOGGER.warning
) from None
# Check if Notarized
@@ -88,6 +91,9 @@ async def cas_validate(
f"Can't parse CodeNotary output: {data!s} - {err!s}", _LOGGER.error
) from err
if _ATTR_ERROR in data_json:
raise CodeNotaryBackendError(data_json[_ATTR_ERROR], _LOGGER.warning)
if data_json[_ATTR_STATUS] == 0:
_CACHE.add((checksum, signer))
else:

View File

@@ -13,7 +13,7 @@ from ..exceptions import WhoamiConnectivityError, WhoamiError, WhoamiSSLError
from .dt import utc_from_timestamp
_LOGGER: logging.Logger = logging.getLogger(__name__)
_API_CALL: str = "whoami.home-assistant.io/v1"
_API_CALL: str = "services.home-assistant.io/whoami/v1"
@attr.s(slots=True, frozen=True)

View File

@@ -41,3 +41,18 @@ def test_v1_folder_migration():
)
assert data[validate.ATTR_FOLDERS] == [validate.FOLDER_ADDONS]
def test_v1_protected():
"""Test v1 protection migration."""
data = validate.SCHEMA_BACKUP(
{
**VALID_DEFAULT,
**{
validate.ATTR_PROTECTED: "8",
validate.ATTR_TYPE: validate.BackupType.FULL,
},
}
)
assert data[validate.ATTR_PROTECTED] is True

View File

@@ -18,8 +18,13 @@ from supervisor.api import RestAPI
from supervisor.bootstrap import initialize_coresys
from supervisor.const import REQUEST_FROM
from supervisor.coresys import CoreSys
from supervisor.dbus.agent import OSAgent
from supervisor.dbus.const import DBUS_SIGNAL_NM_CONNECTION_ACTIVE_CHANGED
from supervisor.dbus.hostname import Hostname
from supervisor.dbus.interface import DBusInterface
from supervisor.dbus.network import NetworkManager
from supervisor.dbus.systemd import Systemd
from supervisor.dbus.timedate import TimeDate
from supervisor.docker import DockerAPI
from supervisor.store.addon import AddonStore
from supervisor.store.repository import Repository
@@ -147,6 +152,37 @@ async def network_manager(dbus) -> NetworkManager:
yield nm_obj
async def mock_dbus_interface(dbus: DBus, instance: DBusInterface) -> DBusInterface:
"""Mock dbus for a DBusInterface instance."""
instance.dbus = dbus
await instance.connect()
return instance
@pytest.fixture
async def hostname(dbus: DBus) -> Hostname:
"""Mock Hostname."""
yield await mock_dbus_interface(dbus, Hostname())
@pytest.fixture
async def timedate(dbus: DBus) -> TimeDate:
"""Mock Timedate."""
yield await mock_dbus_interface(dbus, TimeDate())
@pytest.fixture
async def systemd(dbus: DBus) -> Systemd:
"""Mock Systemd."""
yield await mock_dbus_interface(dbus, Systemd())
@pytest.fixture
async def os_agent(dbus: DBus) -> Systemd:
"""Mock OSAgent."""
yield await mock_dbus_interface(dbus, OSAgent())
@pytest.fixture
async def coresys(loop, docker, network_manager, aiohttp_client, run_dir) -> CoreSys:
"""Create a CoreSys Mock."""

115
tests/dbus/test_resolved.py Normal file
View File

@@ -0,0 +1,115 @@
"""Test systemd-resolved dbus interface."""
from socket import AF_INET6, inet_aton, inet_pton
from unittest.mock import patch
import pytest
from supervisor.coresys import CoreSys
from supervisor.dbus.const import (
DNSOverTLSEnabled,
DNSSECValidation,
DNSStubListenerEnabled,
MulticastProtocolEnabled,
ResolvConfMode,
)
DNS_IP_FIELDS = [
"DNS",
"DNSEx",
"FallbackDNS",
"FallbackDNSEx",
"CurrentDNSServer",
"CurrentDNSServerEx",
]
@pytest.fixture(name="coresys_ip_bytes")
async def fixture_coresys_ip_bytes(coresys: CoreSys) -> CoreSys:
"""Coresys with ip addresses correctly mocked as bytes."""
get_properties = coresys.dbus.network.dbus.get_properties
async def mock_get_properties(dbus_obj, interface):
reply = await get_properties(interface)
for field in DNS_IP_FIELDS:
if field in reply and len(reply[field]) > 0:
if isinstance(reply[field][0], list):
for entry in reply[field]:
entry[2] = bytes(entry[2])
else:
reply[field][2] = bytes(reply[field][2])
return reply
with patch("supervisor.utils.dbus.DBus.get_properties", new=mock_get_properties):
yield coresys
async def test_dbus_resolved_info(coresys_ip_bytes: CoreSys):
"""Test systemd-resolved dbus connection."""
coresys = coresys_ip_bytes
assert coresys.dbus.resolved.dns is None
await coresys.dbus.resolved.connect()
await coresys.dbus.resolved.update()
assert coresys.dbus.resolved.llmnr_hostname == "homeassistant"
assert coresys.dbus.resolved.llmnr == MulticastProtocolEnabled.YES
assert coresys.dbus.resolved.multicast_dns == MulticastProtocolEnabled.RESOLVE
assert coresys.dbus.resolved.dns_over_tls == DNSOverTLSEnabled.NO
assert len(coresys.dbus.resolved.dns) == 2
assert coresys.dbus.resolved.dns[0] == [0, 2, inet_aton("127.0.0.1")]
assert coresys.dbus.resolved.dns[1] == [0, 10, inet_pton(AF_INET6, "::1")]
assert len(coresys.dbus.resolved.dns_ex) == 2
assert coresys.dbus.resolved.dns_ex[0] == [0, 2, inet_aton("127.0.0.1"), 0, ""]
assert coresys.dbus.resolved.dns_ex[1] == [0, 10, inet_pton(AF_INET6, "::1"), 0, ""]
assert len(coresys.dbus.resolved.fallback_dns) == 2
assert coresys.dbus.resolved.fallback_dns[0] == [0, 2, inet_aton("1.1.1.1")]
assert coresys.dbus.resolved.fallback_dns[1] == [
0,
10,
inet_pton(AF_INET6, "2606:4700:4700::1111"),
]
assert len(coresys.dbus.resolved.fallback_dns_ex) == 2
assert coresys.dbus.resolved.fallback_dns_ex[0] == [
0,
2,
inet_aton("1.1.1.1"),
0,
"cloudflare-dns.com",
]
assert coresys.dbus.resolved.fallback_dns_ex[1] == [
0,
10,
inet_pton(AF_INET6, "2606:4700:4700::1111"),
0,
"cloudflare-dns.com",
]
assert coresys.dbus.resolved.current_dns_server == [0, 2, inet_aton("127.0.0.1")]
assert coresys.dbus.resolved.current_dns_server_ex == [
0,
2,
inet_aton("127.0.0.1"),
0,
"",
]
assert len(coresys.dbus.resolved.domains) == 1
assert coresys.dbus.resolved.domains[0] == [0, "local.hass.io", False]
assert coresys.dbus.resolved.transaction_statistics == [0, 100000]
assert coresys.dbus.resolved.cache_statistics == [10, 50000, 10000]
assert coresys.dbus.resolved.dnssec == DNSSECValidation.NO
assert coresys.dbus.resolved.dnssec_statistics == [0, 0, 0, 0]
assert coresys.dbus.resolved.dnssec_supported is False
assert coresys.dbus.resolved.dnssec_negative_trust_anchors == [
"168.192.in-addr.arpa",
"local",
]
assert coresys.dbus.resolved.dns_stub_listener == DNSStubListenerEnabled.NO
assert coresys.dbus.resolved.resolv_conf_mode == ResolvConfMode.FOREIGN

View File

@@ -0,0 +1,52 @@
"""Test Docker interface."""
from unittest.mock import Mock, PropertyMock, call, patch
from awesomeversion import AwesomeVersion
import pytest
from supervisor.const import CpuArch
from supervisor.coresys import CoreSys
from supervisor.docker.interface import DockerInterface
@pytest.fixture(autouse=True)
def mock_verify_content(coresys: CoreSys):
"""Mock verify_content utility during tests."""
with patch.object(
coresys.security, "verify_content", return_value=None
) as verify_content:
yield verify_content
@pytest.mark.parametrize(
"cpu_arch, platform",
[
(CpuArch.ARMV7, "linux/arm/v7"),
(CpuArch.ARMHF, "linux/arm/v6"),
(CpuArch.AARCH64, "linux/arm64"),
(CpuArch.I386, "linux/386"),
(CpuArch.AMD64, "linux/amd64"),
],
)
async def test_docker_image_platform(coresys: CoreSys, cpu_arch: str, platform: str):
"""Test platform set correctly from arch."""
with patch.object(
coresys.docker.images, "pull", return_value=Mock(id="test:1.2.3")
) as pull:
instance = DockerInterface(coresys)
await instance.install(AwesomeVersion("1.2.3"), "test", arch=cpu_arch)
assert pull.call_count == 1
assert pull.call_args == call("test:1.2.3", platform=platform)
async def test_docker_image_default_platform(coresys: CoreSys):
"""Test platform set using supervisor arch when omitted."""
with patch.object(
type(coresys.supervisor), "arch", PropertyMock(return_value="i386")
), patch.object(
coresys.docker.images, "pull", return_value=Mock(id="test:1.2.3")
) as pull:
instance = DockerInterface(coresys)
await instance.install(AwesomeVersion("1.2.3"), "test")
assert pull.call_count == 1
assert pull.call_args == call("test:1.2.3", platform="linux/386")

View File

@@ -1 +1 @@
[]
["/org/freedesktop/NetworkManager/ActiveConnection/1"]

View File

@@ -1 +1,38 @@
[{"connection": {"id": "Wired connection 1", "permissions": [], "timestamp": 1598125548, "type": "802-3-ethernet", "uuid": "0c23631e-2118-355c-bbb0-8943229cb0d6"}, "ipv4": {"address-data": [{"address": "192.168.2.148", "prefix": 24}], "addresses": [[2483202240, 24, 16951488]], "dns": [16951488], "dns-search": [], "gateway": "192.168.2.1", "method": "auto", "route-data": [], "routes": []}, "ipv6": {"address-data": [], "addresses": [], "dns": [], "dns-search": [], "method": "auto", "route-data": [], "routes": []}, "proxy": {}, "802-3-ethernet": {"auto-negotiate": false, "mac-address-blacklist": [], "s390-options": {}}, "802-11-wireless": {"ssid": [78, 69, 84, 84]}}]
[
{
"connection": {
"id": "Wired connection 1",
"interface-name": "eth0",
"permissions": [],
"timestamp": 1598125548,
"type": "802-3-ethernet",
"uuid": "0c23631e-2118-355c-bbb0-8943229cb0d6"
},
"ipv4": {
"address-data": [{ "address": "192.168.2.148", "prefix": 24 }],
"addresses": [[2483202240, 24, 16951488]],
"dns": [16951488],
"dns-search": [],
"gateway": "192.168.2.1",
"method": "auto",
"route-data": [],
"routes": []
},
"ipv6": {
"address-data": [],
"addresses": [],
"dns": [],
"dns-search": [],
"method": "auto",
"route-data": [],
"routes": []
},
"proxy": {},
"802-3-ethernet": {
"auto-negotiate": false,
"mac-address-blacklist": [],
"s390-options": {}
},
"802-11-wireless": { "ssid": [78, 69, 84, 84] }
}
]

View File

@@ -0,0 +1,194 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.resolve1.Manager">
<property name="LLMNRHostname" type="s" access="read">
</property>
<property name="LLMNR" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="MulticastDNS" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSOverTLS" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNS" type="a(iiay)" access="read">
</property>
<property name="DNSEx" type="a(iiayqs)" access="read">
</property>
<property name="FallbackDNS" type="a(iiay)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
<property name="FallbackDNSEx" type="a(iiayqs)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
<property name="CurrentDNSServer" type="(iiay)" access="read">
</property>
<property name="CurrentDNSServerEx" type="(iiayqs)" access="read">
</property>
<property name="Domains" type="a(isb)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="TransactionStatistics" type="(tt)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="CacheStatistics" type="(ttt)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSSEC" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSSECStatistics" type="(tttt)" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSSECSupported" type="b" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSSECNegativeTrustAnchors" type="as" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="DNSStubListener" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<property name="ResolvConfMode" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
<method name="ResolveHostname">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="name" direction="in"/>
<arg type="i" name="family" direction="in"/>
<arg type="t" name="flags" direction="in"/>
<arg type="a(iiay)" name="addresses" direction="out"/>
<arg type="s" name="canonical" direction="out"/>
<arg type="t" name="flags" direction="out"/>
</method>
<method name="ResolveAddress">
<arg type="i" name="ifindex" direction="in"/>
<arg type="i" name="family" direction="in"/>
<arg type="ay" name="address" direction="in"/>
<arg type="t" name="flags" direction="in"/>
<arg type="a(is)" name="names" direction="out"/>
<arg type="t" name="flags" direction="out"/>
</method>
<method name="ResolveRecord">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="name" direction="in"/>
<arg type="q" name="class" direction="in"/>
<arg type="q" name="type" direction="in"/>
<arg type="t" name="flags" direction="in"/>
<arg type="a(iqqay)" name="records" direction="out"/>
<arg type="t" name="flags" direction="out"/>
</method>
<method name="ResolveService">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="name" direction="in"/>
<arg type="s" name="type" direction="in"/>
<arg type="s" name="domain" direction="in"/>
<arg type="i" name="family" direction="in"/>
<arg type="t" name="flags" direction="in"/>
<arg type="a(qqqsa(iiay)s)" name="srv_data" direction="out"/>
<arg type="aay" name="txt_data" direction="out"/>
<arg type="s" name="canonical_name" direction="out"/>
<arg type="s" name="canonical_type" direction="out"/>
<arg type="s" name="canonical_domain" direction="out"/>
<arg type="t" name="flags" direction="out"/>
</method>
<method name="GetLink">
<arg type="i" name="ifindex" direction="in"/>
<arg type="o" name="path" direction="out"/>
</method>
<method name="SetLinkDNS">
<arg type="i" name="ifindex" direction="in"/>
<arg type="a(iay)" name="addresses" direction="in"/>
</method>
<method name="SetLinkDNSEx">
<arg type="i" name="ifindex" direction="in"/>
<arg type="a(iayqs)" name="addresses" direction="in"/>
</method>
<method name="SetLinkDomains">
<arg type="i" name="ifindex" direction="in"/>
<arg type="a(sb)" name="domains" direction="in"/>
</method>
<method name="SetLinkDefaultRoute">
<arg type="i" name="ifindex" direction="in"/>
<arg type="b" name="enable" direction="in"/>
</method>
<method name="SetLinkLLMNR">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="mode" direction="in"/>
</method>
<method name="SetLinkMulticastDNS">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="mode" direction="in"/>
</method>
<method name="SetLinkDNSOverTLS">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="mode" direction="in"/>
</method>
<method name="SetLinkDNSSEC">
<arg type="i" name="ifindex" direction="in"/>
<arg type="s" name="mode" direction="in"/>
</method>
<method name="SetLinkDNSSECNegativeTrustAnchors">
<arg type="i" name="ifindex" direction="in"/>
<arg type="as" name="names" direction="in"/>
</method>
<method name="RevertLink">
<arg type="i" name="ifindex" direction="in"/>
</method>
<method name="RegisterService">
<arg type="s" name="name" direction="in"/>
<arg type="s" name="name_template" direction="in"/>
<arg type="s" name="type" direction="in"/>
<arg type="q" name="service_port" direction="in"/>
<arg type="q" name="service_priority" direction="in"/>
<arg type="q" name="service_weight" direction="in"/>
<arg type="aa{say}" name="txt_datas" direction="in"/>
<arg type="o" name="service_path" direction="out"/>
</method>
<method name="UnregisterService">
<arg type="o" name="service_path" direction="in"/>
</method>
<method name="ResetStatistics">
</method>
<method name="FlushCaches">
</method>
<method name="ResetServerFeatures">
</method>
</interface>
<node name="link"/>
<node name="dnssd"/>
</node>

View File

@@ -0,0 +1,39 @@
{
"LLMNRHostname": "homeassistant",
"LLMNR": "yes",
"MulticastDNS": "resolve",
"DNSOverTLS": "no",
"DNS": [
[0, 2, [127, 0, 0, 1]],
[0, 10, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]
],
"DNSEx": [
[0, 2, [127, 0, 0, 1], 0, ""],
[0, 10, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 0, ""]
],
"FallbackDNS": [
[0, 2, [1, 1, 1, 1]],
[0, 10, [38, 6, 71, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17]]
],
"FallbackDNSEx": [
[0, 2, [1, 1, 1, 1], 0, "cloudflare-dns.com"],
[
0,
10,
[38, 6, 71, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17],
0,
"cloudflare-dns.com"
]
],
"CurrentDNSServer": [0, 2, [127, 0, 0, 1]],
"CurrentDNSServerEx": [0, 2, [127, 0, 0, 1], 0, ""],
"Domains": [[0, "local.hass.io", false]],
"TransactionStatistics": [0, 100000],
"CacheStatistics": [10, 50000, 10000],
"DNSSEC": "no",
"DNSSECStatistics": [0, 0, 0, 0],
"DNSSECSupported": false,
"DNSSECNegativeTrustAnchors": ["168.192.in-addr.arpa", "local"],
"DNSStubListener": "no",
"ResolvConfMode": "foreign"
}

View File

@@ -0,0 +1,50 @@
[
[
"etc-machine\\x2did.mount",
"/etc/machine-id",
"loaded",
"active",
"mounted",
"",
"/org/freedesktop/systemd1/unit/etc_2dmachine_5cx2did_2emount",
0,
"",
"/"
],
[
"firewalld.service",
"firewalld.service",
"not-found",
"inactive",
"dead",
"",
"/org/freedesktop/systemd1/unit/firewalld_2eservice",
0,
"",
"/"
],
[
"sys-devices-virtual-tty-ttypd.device",
"/sys/devices/virtual/tty/ttypd",
"loaded",
"active",
"plugged",
"",
"/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dtty_2dttypd_2edevice",
0,
"",
"/"
],
[
"zram-swap.service",
"HassOS ZRAM swap",
"loaded",
"active",
"exited",
"",
"/org/freedesktop/systemd1/unit/zram_2dswap_2eservice",
0,
"",
"/"
]
]

View File

@@ -0,0 +1,127 @@
{
"Version": "249",
"Features": "+PAM -AUDIT -SELINUX +APPARMOR -IMA -SMACK -SECCOMP +GCRYPT +GNUTLS +OPENSSL -ACL +BLKID +CURL -ELFUTILS -FIDO2 -IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE -BZIP2 -LZ4 -XZ +ZLIB -ZSTD -XKBCOMMON -UTMP -SYSVINIT default-hierarchy=hybrid",
"Virtualization": "",
"Architecture": "arm64",
"Tainted": "cgroupsv1",
"FirmwareTimestamp": 0,
"FirmwareTimestampMonotonic": 0,
"LoaderTimestamp": 0,
"LoaderTimestampMonotonic": 0,
"KernelTimestamp": 1646197924245019,
"KernelTimestampMonotonic": 0,
"InitRDTimestamp": 0,
"InitRDTimestampMonotonic": 0,
"UserspaceTimestamp": 1646197926126937,
"UserspaceTimestampMonotonic": 1881921,
"FinishTimestamp": 1646197962613554,
"FinishTimestampMonotonic": 38368540,
"SecurityStartTimestamp": 1646197926137295,
"SecurityStartTimestampMonotonic": 1892280,
"SecurityFinishTimestamp": 1646197926139253,
"SecurityFinishTimestampMonotonic": 1894237,
"GeneratorsStartTimestamp": 1646197926235939,
"GeneratorsStartTimestampMonotonic": 1990923,
"GeneratorsFinishTimestamp": 1646197926260378,
"GeneratorsFinishTimestampMonotonic": 2015363,
"UnitsLoadStartTimestamp": 1646197926260388,
"UnitsLoadStartTimestampMonotonic": 2015371,
"UnitsLoadFinishTimestamp": 1646197926339294,
"UnitsLoadFinishTimestampMonotonic": 2094278,
"InitRDSecurityStartTimestamp": 0,
"InitRDSecurityStartTimestampMonotonic": 0,
"InitRDSecurityFinishTimestamp": 0,
"InitRDSecurityFinishTimestampMonotonic": 0,
"InitRDGeneratorsStartTimestamp": 0,
"InitRDGeneratorsStartTimestampMonotonic": 0,
"InitRDGeneratorsFinishTimestamp": 0,
"InitRDGeneratorsFinishTimestampMonotonic": 0,
"InitRDUnitsLoadStartTimestamp": 0,
"InitRDUnitsLoadStartTimestampMonotonic": 0,
"InitRDUnitsLoadFinishTimestamp": 0,
"InitRDUnitsLoadFinishTimestampMonotonic": 0,
"LogLevel": "info",
"LogTarget": "journal - or - kmsg",
"NNames": 377,
"NFailedUnits": 0,
"NJobs": 0,
"NInstalledJobs": 798,
"NFailedJobs": 0,
"Progress": 1.0,
"Environment": [
"LANG = C.UTF - 8",
"PATH = /usr/local / sbin: /usr/local / bin: /usr/sbin: /usr/bin"
],
"ConfirmSpawn": false,
"ShowStatus": true,
"UnitPath": [
" / etc / systemd / system.control",
" / run / systemd / system.control",
" / run / systemd / transient",
" / run / systemd / generator.early",
" / etc / systemd / system",
" / etc / systemd / system.attached",
" / run / systemd / system",
" / run / systemd / system.attached",
" / run / systemd / generator",
" / usr / local / lib / systemd / system",
" / usr / lib / systemd / system",
" / run / systemd / generator.late"
],
"DefaultStandardOutput": "journal",
"DefaultStandardError": "inherit",
"RuntimeWatchdogUSec": 0,
"RebootWatchdogUSec": 600000000,
"KExecWatchdogUSec": 0,
"ServiceWatchdogs": true,
"ControlGroup": "",
"SystemState": "running",
"ExitCode": [0, 0, 0, 0],
"DefaultTimerAccuracyUSec": 60000000,
"DefaultTimeoutStartUSec": 90000000,
"DefaultTimeoutStopUSec": 90000000,
"DefaultTimeoutAbortUSec": 90000000,
"DefaultRestartUSec": 100000,
"DefaultStartLimitIntervalUSec": 10000000,
"DefaultStartLimitBurst": 5,
"DefaultCPUAccounting": false,
"DefaultBlockIOAccounting": false,
"DefaultMemoryAccounting": true,
"DefaultTasksAccounting": true,
"DefaultLimitCPU": 18446744073709551615,
"DefaultLimitCPUSoft": 18446744073709551615,
"DefaultLimitFSIZE": 18446744073709551615,
"DefaultLimitFSIZESoft": 18446744073709551615,
"DefaultLimitDATA": 18446744073709551615,
"DefaultLimitDATASoft": 18446744073709551615,
"DefaultLimitSTACK": 18446744073709551615,
"DefaultLimitSTACKSoft": 8388608,
"DefaultLimitCORE": 18446744073709551615,
"DefaultLimitCORESoft": 18446744073709551615,
"DefaultLimitRSS": 18446744073709551615,
"DefaultLimitRSSSoft": 18446744073709551615,
"DefaultLimitNOFILE": 524288,
"DefaultLimitNOFILESoft": 1024,
"DefaultLimitAS": 18446744073709551615,
"DefaultLimitASSoft": 18446744073709551615,
"DefaultLimitNPROC": 14236,
"DefaultLimitNPROCSoft": 14236,
"DefaultLimitMEMLOCK": 65536,
"DefaultLimitMEMLOCKSoft": 65536,
"DefaultLimitLOCKS": 18446744073709551615,
"DefaultLimitLOCKSSoft": 18446744073709551615,
"DefaultLimitSIGPENDING": 14236,
"DefaultLimitSIGPENDINGSoft": 14236,
"DefaultLimitMSGQUEUE": 819200,
"DefaultLimitMSGQUEUESoft": 819200,
"DefaultLimitNICE": 0,
"DefaultLimitNICESoft": 0,
"DefaultLimitRTPRIO": 0,
"DefaultLimitRTPRIOSoft": 0,
"DefaultLimitRTTIME": 18446744073709551615,
"DefaultLimitRTTIMESoft": 18446744073709551615,
"DefaultTasksMax": 4270,
"TimerSlackNSec": 50000,
"DefaultOOMPolicy": "stop",
"CtrlAltDelBurstAction": "reboot - force"
}

View File

@@ -0,0 +1,78 @@
"""Test host manager."""
from unittest.mock import AsyncMock, PropertyMock, patch
from supervisor.coresys import CoreSys
from supervisor.dbus.agent import OSAgent
from supervisor.dbus.hostname import Hostname
from supervisor.dbus.systemd import Systemd
from supervisor.dbus.timedate import TimeDate
async def test_reload(coresys: CoreSys):
"""Test manager reload."""
with patch.object(coresys.host.info, "update") as info_update, patch.object(
coresys.host.services, "update"
) as services_update, patch.object(
coresys.host.network, "update"
) as network_update, patch.object(
coresys.host.sys_dbus.agent, "update", new=AsyncMock()
) as agent_update, patch.object(
coresys.host.sound, "update"
) as sound_update:
await coresys.host.reload()
info_update.assert_called_once()
services_update.assert_called_once()
network_update.assert_called_once()
agent_update.assert_called_once()
sound_update.assert_called_once()
info_update.reset_mock()
services_update.reset_mock()
network_update.reset_mock()
agent_update.reset_mock()
sound_update.reset_mock()
await coresys.host.reload(
services=False, network=False, agent=False, audio=False
)
info_update.assert_called_once()
services_update.assert_not_called()
network_update.assert_not_called()
agent_update.assert_not_called()
sound_update.assert_not_called()
async def test_load(
coresys: CoreSys,
hostname: Hostname,
systemd: Systemd,
timedate: TimeDate,
os_agent: OSAgent,
):
"""Test manager load."""
type(coresys.dbus).hostname = PropertyMock(return_value=hostname)
type(coresys.dbus).systemd = PropertyMock(return_value=systemd)
type(coresys.dbus).timedate = PropertyMock(return_value=timedate)
type(coresys.dbus).agent = PropertyMock(return_value=os_agent)
with patch.object(coresys.host.sound, "update") as sound_update, patch.object(
coresys.host.apparmor, "load"
) as apparmor_load:
# Network is updated on connect for a version check so its not None already
assert coresys.dbus.hostname.hostname is None
assert coresys.dbus.systemd.boot_timestamp is None
assert coresys.dbus.timedate.timezone is None
assert coresys.dbus.agent.diagnostics is None
await coresys.host.load()
assert coresys.dbus.hostname.hostname == "homeassistant-n2"
assert coresys.dbus.systemd.boot_timestamp == 1646197962613554
assert coresys.dbus.timedate.timezone == "Etc/UTC"
assert coresys.dbus.agent.diagnostics is True
assert coresys.dbus.network.connectivity_enabled is True
sound_update.assert_called_once()
apparmor_load.assert_called_once()

View File

@@ -0,0 +1,31 @@
"""Test network manager."""
from unittest.mock import Mock, patch
from supervisor.coresys import CoreSys
async def test_load(coresys: CoreSys):
"""Test network manager load."""
with patch.object(
coresys.host.sys_dbus.network,
"activate_connection",
new=Mock(wraps=coresys.host.sys_dbus.network.activate_connection),
) as activate_connection:
await coresys.host.network.load()
assert coresys.host.network.connectivity is True
assert len(coresys.host.network.dns_servers) == 1
assert str(coresys.host.network.dns_servers[0]) == "192.168.30.1"
assert len(coresys.host.network.interfaces) == 2
assert coresys.host.network.interfaces[0].name == "eth0"
assert coresys.host.network.interfaces[0].enabled is True
assert coresys.host.network.interfaces[1].name == "wlan0"
assert coresys.host.network.interfaces[1].enabled is False
assert activate_connection.call_count == 1
assert activate_connection.call_args.args == (
"/org/freedesktop/NetworkManager/Settings/1",
"/org/freedesktop/NetworkManager/Devices/1",
)

View File

@@ -0,0 +1,49 @@
"""Test evaluate systemd-resolved."""
from unittest.mock import PropertyMock, patch
from supervisor.const import CoreState
from supervisor.coresys import CoreSys
from supervisor.resolution.evaluations.resolved import EvaluateResolved
async def test_evaluation(coresys: CoreSys):
"""Test evaluation."""
resolved = EvaluateResolved(coresys)
coresys.core.state = CoreState.SETUP
assert resolved.reason not in coresys.resolution.unsupported
with patch.object(
type(coresys.dbus.resolved), "is_connected", PropertyMock(return_value=False)
):
await resolved()
assert resolved.reason in coresys.resolution.unsupported
await resolved()
assert resolved.reason not in coresys.resolution.unsupported
async def test_did_run(coresys: CoreSys):
"""Test that the evaluation ran as expected."""
resolved = EvaluateResolved(coresys)
should_run = resolved.states
should_not_run = [state for state in CoreState if state not in should_run]
assert len(should_run) != 0
assert len(should_not_run) != 0
with patch(
"supervisor.resolution.evaluations.resolved.EvaluateResolved.evaluate",
return_value=None,
) as evaluate:
for state in should_run:
coresys.core.state = state
await resolved()
evaluate.assert_called_once()
evaluate.reset_mock()
for state in should_not_run:
coresys.core.state = state
await resolved()
evaluate.assert_not_called()
evaluate.reset_mock()

View File

@@ -15,13 +15,6 @@ async def test_content_trust(coresys: CoreSys):
assert cas_validate.called
cas_validate.assert_called_once_with("test@mail.com", "ffffffffffffff")
with patch("supervisor.security.cas_validate", AsyncMock()) as cas_validate:
await coresys.security.verify_own_content("ffffffffffffff")
assert cas_validate.called
cas_validate.assert_called_once_with(
"notary@home-assistant.io", "ffffffffffffff"
)
async def test_disabled_content_trust(coresys: CoreSys):
"""Test Content-Trust."""
@@ -31,10 +24,6 @@ async def test_disabled_content_trust(coresys: CoreSys):
await coresys.security.verify_content("test@mail.com", "ffffffffffffff")
assert not cas_validate.called
with patch("supervisor.security.cas_validate", AsyncMock()) as cas_validate:
await coresys.security.verify_own_content("ffffffffffffff")
assert not cas_validate.called
async def test_force_content_trust(coresys: CoreSys):
"""Force Content-Trust tests."""

View File

@@ -1,11 +1,51 @@
"""Test CodeNotary."""
from __future__ import annotations
from dataclasses import dataclass
from unittest.mock import AsyncMock, Mock, patch
import pytest
from supervisor.exceptions import CodeNotaryUntrusted
from supervisor.exceptions import (
CodeNotaryBackendError,
CodeNotaryError,
CodeNotaryUntrusted,
)
from supervisor.utils.codenotary import calc_checksum, cas_validate
@dataclass
class SubprocessResponse:
"""Class for specifying subprocess exec response."""
returncode: int = 0
data: str = ""
error: str | None = None
exception: Exception | None = None
@pytest.fixture(name="subprocess_exec")
def fixture_subprocess_exec(request):
"""Mock subprocess exec with specific return."""
response = request.param
if response.exception:
communicate_return = AsyncMock(side_effect=response.exception)
else:
err_return = None
if response.error:
err_return = Mock(decode=Mock(return_value=response.error))
communicate_return = AsyncMock(return_value=(response.data, err_return))
exec_return = Mock(returncode=response.returncode, communicate=communicate_return)
with patch(
"supervisor.utils.codenotary.asyncio.create_subprocess_exec",
return_value=exec_return,
) as subprocess_exec:
yield subprocess_exec
def test_checksum_calc():
"""Calc Checkusm as test."""
assert calc_checksum("test") == calc_checksum(b"test")
@@ -30,3 +70,46 @@ async def test_invalid_checksum():
"notary@home-assistant.io",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)
@pytest.mark.parametrize(
"subprocess_exec",
[
SubprocessResponse(returncode=1, error="test"),
SubprocessResponse(returncode=0, data='{"error":"asn1: structure error"}'),
],
indirect=True,
)
async def test_cas_backend_error(subprocess_exec):
"""Test backend error executing cas command."""
with pytest.raises(CodeNotaryBackendError):
await cas_validate(
"notary@home-assistant.io",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)
@pytest.mark.parametrize(
"subprocess_exec",
[SubprocessResponse(returncode=0, data='{"status":1}')],
indirect=True,
)
async def test_cas_notarized_untrusted(subprocess_exec):
"""Test cas found notarized but untrusted content."""
with pytest.raises(CodeNotaryUntrusted):
await cas_validate(
"notary@home-assistant.io",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)
@pytest.mark.parametrize(
"subprocess_exec", [SubprocessResponse(exception=OSError())], indirect=True
)
async def test_cas_exec_os_error(subprocess_exec):
"""Test os error attempting to execute cas command."""
with pytest.raises(CodeNotaryError):
await cas_validate(
"notary@home-assistant.io",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
)