* Stop reading advanced logs on ConnectionError
If the client side connection closes with a `ConnectionError`, stop
reading the advanced logs.
This is very similar to ClientConnectionResetError which is easily
reproducable by having a log open and following in the browser and
then restaring Home Assistant. So far I wans't able to artificaially
reproduce the ConnectionError, but there are quite some reports on
Sentry so it seems to happen in real world.
* Warn on ConnectionError
* feat: Add IPv6 address generation mode & privacy extensions
Signed-off-by: David Rapan <david@rapan.cz>
* Use NetworkManager fixture for settings init tests
This fixes the test by since the extended implementation now can read
the version of NetworkManager.
* Add pytest for addr_gen_mode
---------
Signed-off-by: David Rapan <david@rapan.cz>
Co-authored-by: Stefan Agner <stefan@agner.ch>
* Trigger auto-update through Core WebSocket call
Instead of auto-updating add-ons on Supervisor side trigger an update
through Core via a WebSocket command. This makes sure that the backup
is categorized correctly and all backup features like retention are
applied.
* Add pytest
* Fix pytest
* Fix pytest
* Fix pytest
* Fix pytest
* Fix pytest cleaner
* Set timestamp of add-on far into the past
Instead of copying the backup in the main job, lets copy them in
separate job per location. This allows to use the same backup error
handling mechanism as for add-ons and folders.
This makes the stage introduced in #5784 somewhat redundant, but
before removing it, let's see if this approach works out.
* Harmonize folder and add-on backup error handling
Align add-on and folder backup error handling in that in both cases
errors are recorded on the respective backup Jobs, but not raised to
the caller. This allows the backup to complete successfully even if
some add-ons or folders fail to back up.
Along with this, also record errors in the per-add-on and per-folder
backup jobs, as well as the add-on and folder root job.
And finally, align the exception handling to only catch expected
exceptions for add-ons too.
* Fix pytest
In case the c-ares based AsyncResolver fails to initialize, let's
fallback to the threaded resolver. This would have helped for aiodns
3.3.0 issue when clients were unable to allocate more inotify watches.
This is fixed in aiodns 3.4.0, but we should still fallback to the
threaded resolver as a precautionary measure.
* Handle non-existing addon config dir
Since users have access to the root of all add-on config directories,
they can delete the directory of an add-ons at any time. Hence we need
to handle gracefully if it doesn't exist anymore.
* Add pytest
When the systemd-journal-gatewayd service is being shutdown while
Supervisor is still trying to read logs, aiohttp throws a
ClientPayloadError, presumably because we try to read until the next
linefeed, which aiohttp cannot satisfy anymore.
Simply catch the exception just like the connection reset errors
previously in #5358 and #5715.
* Recreate aiohttp ClientSession after DNS plug-in load
Create a temporary ClientSession early in case we need to load version
information from the internet. This doesn't use the final DNS setup
and hence might fail to load in certain situations since we don't have
the fallback mechanims in place yet. But if the DNS container image
is present, we'll continue the setup and load the DNS plug-in. We then
can recreate the ClientSession such that it uses the DNS plug-in.
This works around an issue with aiodns, which today doesn't reload
`resolv.conf` automatically when it changes. This lead to Supervisor
using the initial `resolv.conf` as created by Docker. It meant that
we did not use the DNS plug-in (and its fallback capabilities) in
Supervisor. Also it meant that changes to the DNS setup at runtime
did not propagate to the aiohttp ClientSession (as observed in #5332).
* Mock aiohttp.ClientSession for all tests
Currently in several places pytest actually uses the aiohttp
ClientSession and reaches out to the internet. This is not ideal
for unit tests and should be avoided.
This creates several new fixtures to aid this effort: The `websession`
fixture simply returns a mocked aiohttp.ClientSession, which can be
used whenever a function is tested which needs the global websession.
A separate new fixture to mock the connectivity check named
`supervisor_internet` since this is often used through the Job
decorator which require INTERNET_SYSTEM.
And the `mock_update_data` uses the already existing update json
test data from the fixture directory instead of loading the data
from the internet.
* Log ClientSession nameserver information
When recreating the aiohttp ClientSession, log information what
nameservers exactly are going to be used.
* Refuse ClientSession initialization when API is available
Previous attempts to reinitialize the ClientSession have shown
use of the ClientSession after it was closed due to API requets
being handled in parallel to the reinitialization (see #5851).
Make sure this is not possible by refusing to reinitialize the
ClientSession when the API is available.
* Fix pytests
Also sure we don't create aiohttp ClientSession objects unnecessarily.
* Apply suggestions from code review
Co-authored-by: Jan Čermák <sairon@users.noreply.github.com>
---------
Co-authored-by: Jan Čermák <sairon@users.noreply.github.com>
Similar to #5825, make sure we mock the systemd journal gateway socket
for tests. This makes the test work on systems which have
systemd-journal-gatewayd installed.
* Check local store repository for changes
Instead of simply assume that the local store repository got changed,
use mtime to check if there have been any changes to the local store.
This mimics a similar behavior to the git repository store updates.
Before this change, we end up in the updated repo code path, which
caused a re-read of all add-ons on every store reload, even though
nothing changed at all. Store reloads are triggered by Home Assistant
Core every 5 minutes.
* Fix pytest failure
Now that we actually only reload metadata if the local store changed
we have to fake the change as well to fix the store manager tests.
* Fix path cache update test for local store repository
* Take root directory into account/add pytest
* Rename utils/__init__.py tests to test_utils_init.py
When uninstalling an add-on, we schedule a task to reload the ingress
tokens. This scheduled task typically ends up running right after
clearing the add-on data with `self.sys_addons.data.uninstall(self)`
(since this task is doing I/O, the race is rather deterministic).
Let's make sure we reload the ingress tokens at the end. Also simply
execute reloading synchrounsly since this is a rather quick operation
and makes sure that errors would get attributed to the right add-on
uninstall operation.
* Improve backup upload location determination
For local backup upload locations, check if the location is on the same
file system an thuse allows to move the backup file after upload. This
allows custom backup mounts. Currently there is no documented,
persistent way to create such mounts in with Home Assistant OS
installations, but since we might add local mounts in the future this
seems a worthwhile addition.
Fixes: #5837
* Fix pytests
So far a store reload lead to a reload of all add-ons twice, usually
causing two messages in quick succession:
```
2025-04-25 17:01:05.058 INFO (MainThread) [supervisor.store] Loading add-ons from store: 91 all - 0 new - 0 remove
2025-04-25 17:01:05.058 INFO (MainThread) [supervisor.store] Loading add-ons from store: 91 all - 0 new - 0 remove
```
This is because when repository changes are detected, `reload()` calls
`load()` which then calls `update_repositories()` which ends up calling
`_read_addons()`, while `reload()` itself calls `_read_addons()` after
`load()` as well.
One way to fix this would be to simply remove the `_read_addons()` call
in `reload()`.
However, it seems the `update_repositories()` call (via `load()`)
is not necessary at all, as we don't add new store repositories in
`reload()`, and we already made sure the built-ins are present on
startup.
So simply call `data.update()` to update the store data cache, as it
was the case before #2225. There is no apparent reason documented why
`data.update()` was changed to a `load()` call. It might be to ensure
regularly that built-in repositories are still in the list of store
repositories. But this type of regular invariant check is often harmful
as it might hide bugs in other places.
Supervisor will still call `update_repositories()` in `load()` to
ensure all built-in repositories are present, just in case the local
configuration file got modified or corrupted.
This reverts commit 1504278223.
It turns out that recreating the session can cause race conditions, e.g.
with API checks triggered by proxied requests running alongside. These
manifest in the following error:
AttributeError: 'NoneType' object has no attribute 'connect'
...
File "supervisor/homeassistant/api.py", line 187, in check_api_state
if state := await self.get_api_state():
File "supervisor/homeassistant/api.py", line 171, in get_api_state
data = await self.get_core_state()
File "supervisor/homeassistant/api.py", line 145, in get_core_state
return await self._get_json("api/core/state")
File "supervisor/homeassistant/api.py", line 132, in _get_json
async with self.make_request("get", path) as resp:
File "contextlib.py", line 214, in __aenter__
return await anext(self.gen)
File "supervisor/homeassistant/api.py", line 106, in make_request
async with getattr(self.sys_websession, method)(
File "aiohttp/client.py", line 1425, in __aenter__
self._resp: _RetType = await self._coro
File "aiohttp/client.py", line 703, in _request
conn = await self._connector.connect(
The only reason for the _connection in the aiohttp client to be None is
when close() gets called on the session. The only place this is being
done is the connectivity check.
So it seems that between fetching the session from the `sys_websession`
property) and actually using the connector, a connectivity check has been
run which then causes the above stack trace.
Let's not mess with the lifetime of the ClientSession object and simply
revert the change. Another solution for the original problem needs to be
found.
* Add basic test coverage for /auth API
* Check /auth API is called from an add-on
Currently the /auth API is only available for add-ons. Return 403
for calls not originating from an add-on.
* Handle bad json in auth API
Use the API specific JSON load helper which raises an APIError. This
causes the API to return a 400 error instead of a 500 error when the
JSON is invalid.
* Avoid redefining name 'mock_check_login'
* Update tests/api/test_auth.py
* Add dedicated update information reload
Currently we have the /refresh_updates endpoint which updates the main
component versions (Core, OS, Supervisor, Plug-ins) and the add-on
store at the same time. This combined update causes more update
information reloads than necessary.
To allow fine grained update refresh control introduce a new endpoint
/reload_updates which asks Supervisor to only update main component
versions (learned through the version json files).
The /store/reload endpoint already allows to update the add-on store
separately.
* Add pytest
* Update supervisor/api/__init__.py