mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-01-02 11:03:57 +00:00
579ade1ed8
The backup module is part of the default integrations and as such it will always be loaded. Replace it with the prometheus module, for which this is probably unlikely to ever become the case.
246 lines
9.0 KiB
Nix
246 lines
9.0 KiB
Nix
import ./make-test-python.nix ({ pkgs, lib, ... }:
|
|
|
|
let
|
|
configDir = "/var/lib/foobar";
|
|
in {
|
|
name = "home-assistant";
|
|
meta.maintainers = lib.teams.home-assistant.members;
|
|
|
|
nodes.hass = { pkgs, ... }: {
|
|
services.postgresql = {
|
|
enable = true;
|
|
ensureDatabases = [ "hass" ];
|
|
ensureUsers = [{
|
|
name = "hass";
|
|
ensureDBOwnership = true;
|
|
}];
|
|
};
|
|
|
|
services.home-assistant = {
|
|
enable = true;
|
|
inherit configDir;
|
|
|
|
# provide dependencies through package overrides
|
|
package = (pkgs.home-assistant.override {
|
|
extraPackages = ps: with ps; [
|
|
colorama
|
|
];
|
|
extraComponents = [
|
|
# test char-tty device allow propagation into the service
|
|
"zha"
|
|
];
|
|
});
|
|
|
|
# provide component dependencies explicitly from the module
|
|
extraComponents = [
|
|
"mqtt"
|
|
];
|
|
|
|
# provide package for postgresql support
|
|
extraPackages = python3Packages: with python3Packages; [
|
|
psycopg2
|
|
];
|
|
|
|
# test loading custom components
|
|
customComponents = with pkgs.home-assistant-custom-components; [
|
|
prometheus_sensor
|
|
# tests loading multiple components from a single package
|
|
spook
|
|
];
|
|
|
|
# test loading lovelace modules
|
|
customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [
|
|
mini-graph-card
|
|
];
|
|
|
|
config = {
|
|
homeassistant = {
|
|
name = "Home";
|
|
time_zone = "UTC";
|
|
latitude = "0.0";
|
|
longitude = "0.0";
|
|
elevation = 0;
|
|
};
|
|
|
|
# configure the recorder component to use the postgresql db
|
|
recorder.db_url = "postgresql://@/hass";
|
|
|
|
# we can't load default_config, because the updater requires
|
|
# network access and would cause an error, so load frontend
|
|
# here explicitly.
|
|
# https://www.home-assistant.io/integrations/frontend/
|
|
frontend = {};
|
|
|
|
# include some popular integrations, that absolutely shouldn't break
|
|
knx = {};
|
|
shelly = {};
|
|
zha = {};
|
|
|
|
# set up a wake-on-lan switch to test capset capability required
|
|
# for the ping suid wrapper
|
|
# https://www.home-assistant.io/integrations/wake_on_lan/
|
|
switch = [ {
|
|
platform = "wake_on_lan";
|
|
mac = "00:11:22:33:44:55";
|
|
host = "127.0.0.1";
|
|
} ];
|
|
|
|
# test component-based capability assignment (CAP_NET_BIND_SERVICE)
|
|
# https://www.home-assistant.io/integrations/emulated_hue/
|
|
emulated_hue = {
|
|
host_ip = "127.0.0.1";
|
|
listen_port = 80;
|
|
};
|
|
|
|
# https://www.home-assistant.io/integrations/logger/
|
|
logger = {
|
|
default = "info";
|
|
};
|
|
};
|
|
|
|
# configure the sample lovelace dashboard
|
|
lovelaceConfig = {
|
|
title = "My Awesome Home";
|
|
views = [{
|
|
title = "Example";
|
|
cards = [{
|
|
type = "markdown";
|
|
title = "Lovelace";
|
|
content = "Welcome to your **Lovelace UI**.";
|
|
}];
|
|
}];
|
|
};
|
|
lovelaceConfigWritable = true;
|
|
};
|
|
|
|
# Cause a configuration change inside `configuration.yml` and verify that the process is being reloaded.
|
|
specialisation.differentName = {
|
|
inheritParentConfig = true;
|
|
configuration.services.home-assistant.config.homeassistant.name = lib.mkForce "Test Home";
|
|
};
|
|
|
|
# Cause a configuration change that requires a service restart as we added a new runtime dependency
|
|
specialisation.newFeature = {
|
|
inheritParentConfig = true;
|
|
configuration.services.home-assistant.config.prometheus = {};
|
|
};
|
|
|
|
specialisation.removeCustomThings = {
|
|
inheritParentConfig = true;
|
|
configuration.services.home-assistant = {
|
|
customComponents = lib.mkForce [];
|
|
customLovelaceModules = lib.mkForce [];
|
|
};
|
|
};
|
|
};
|
|
|
|
testScript = { nodes, ... }: let
|
|
system = nodes.hass.system.build.toplevel;
|
|
in
|
|
''
|
|
import json
|
|
|
|
start_all()
|
|
|
|
|
|
def get_journal_cursor() -> str:
|
|
exit, out = hass.execute("journalctl -u home-assistant.service -n1 -o json-pretty --output-fields=__CURSOR")
|
|
assert exit == 0
|
|
return json.loads(out)["__CURSOR"]
|
|
|
|
|
|
def get_journal_since(cursor) -> str:
|
|
exit, out = hass.execute(f"journalctl --after-cursor='{cursor}' -u home-assistant.service")
|
|
assert exit == 0
|
|
return out
|
|
|
|
|
|
def get_unit_property(property) -> str:
|
|
exit, out = hass.execute(f"systemctl show --property={property} home-assistant.service")
|
|
assert exit == 0
|
|
return out
|
|
|
|
|
|
def wait_for_homeassistant(cursor):
|
|
hass.wait_until_succeeds(f"journalctl --after-cursor='{cursor}' -u home-assistant.service | grep -q 'Home Assistant initialized in'")
|
|
|
|
|
|
hass.wait_for_unit("home-assistant.service")
|
|
cursor = get_journal_cursor()
|
|
|
|
with subtest("Check that YAML configuration file is in place"):
|
|
hass.succeed("test -L ${configDir}/configuration.yaml")
|
|
|
|
with subtest("Check the lovelace config is copied because lovelaceConfigWritable = true"):
|
|
hass.succeed("test -f ${configDir}/ui-lovelace.yaml")
|
|
|
|
with subtest("Check that Home Assistant's web interface and API can be reached"):
|
|
wait_for_homeassistant(cursor)
|
|
hass.wait_for_open_port(8123)
|
|
hass.succeed("curl --fail http://localhost:8123/lovelace")
|
|
|
|
with subtest("Check that custom components get installed"):
|
|
hass.succeed("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json")
|
|
for integration in ("prometheus_sensor", "spook", "spook_inverse"):
|
|
hass.wait_until_succeeds(f"journalctl -u home-assistant.service | grep -q 'We found a custom integration {integration} which has not been tested by Home Assistant'")
|
|
|
|
with subtest("Check that lovelace modules are referenced and fetchable"):
|
|
hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/configuration.yaml'")
|
|
hass.succeed("curl --fail http://localhost:8123/local/nixos-lovelace-modules/mini-graph-card-bundle.js")
|
|
|
|
with subtest("Check that optional dependencies are in the PYTHONPATH"):
|
|
env = get_unit_property("Environment")
|
|
python_path = env.split("PYTHONPATH=")[1].split()[0]
|
|
for package in ["colorama", "paho-mqtt", "psycopg2"]:
|
|
assert package in python_path, f"{package} not in PYTHONPATH"
|
|
|
|
with subtest("Check that declaratively configured components get setup"):
|
|
journal = get_journal_since(cursor)
|
|
for domain in ["emulated_hue", "wake_on_lan"]:
|
|
assert f"Setup of domain {domain} took" in journal, f"{domain} setup missing"
|
|
|
|
with subtest("Check that capabilities are passed for emulated_hue to bind to port 80"):
|
|
hass.wait_for_open_port(80)
|
|
hass.succeed("curl --fail http://localhost:80/description.xml")
|
|
|
|
with subtest("Check extra components are considered in systemd unit hardening"):
|
|
hass.succeed("systemctl show -p DeviceAllow home-assistant.service | grep -q char-ttyUSB")
|
|
|
|
with subtest("Check service restart from SIGHUP"):
|
|
pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
|
|
cursor = get_journal_cursor()
|
|
hass.succeed("${system}/specialisation/differentName/bin/switch-to-configuration test")
|
|
wait_for_homeassistant(cursor)
|
|
new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
|
|
assert pid != new_pid, "The PID of the process must change after sending SIGHUP"
|
|
|
|
with subtest("Check service restarts when dependencies change"):
|
|
pid = new_pid
|
|
cursor = get_journal_cursor()
|
|
hass.succeed("${system}/specialisation/newFeature/bin/switch-to-configuration test")
|
|
wait_for_homeassistant(cursor)
|
|
new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
|
|
assert pid != new_pid, "The PID of the process must change when its PYTHONPATH changess"
|
|
|
|
with subtest("Check that new components get setup after restart"):
|
|
journal = get_journal_since(cursor)
|
|
for domain in ["prometheus"]:
|
|
assert f"Setup of domain {domain} took" in journal, f"{domain} setup missing"
|
|
|
|
with subtest("Check custom components and custom lovelace modules get removed"):
|
|
cursor = get_journal_cursor()
|
|
hass.succeed("${system}/specialisation/removeCustomThings/bin/switch-to-configuration test")
|
|
hass.fail("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'")
|
|
for integration in ("prometheus_sensor", "spook", "spook_inverse"):
|
|
hass.fail(f"test -f ${configDir}/custom_components/{integration}/manifest.json")
|
|
wait_for_homeassistant(cursor)
|
|
|
|
with subtest("Check that no errors were logged"):
|
|
hass.fail("journalctl -u home-assistant -o cat | grep -q ERROR")
|
|
|
|
with subtest("Check systemd unit hardening"):
|
|
hass.log(hass.succeed("systemctl cat home-assistant.service"))
|
|
hass.log(hass.succeed("systemd-analyze security home-assistant.service"))
|
|
'';
|
|
})
|