Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/home-assistant: fix bug causing config file not to be written (including http.server_port) #82480

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions nixos/modules/services/misc/home-assistant.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ with lib;
let
cfg = config.services.home-assistant;

# cfg.config != null can be assumed here
# Combined config
hassConfig = recursiveUpdate
(optionalAttrs cfg.applyDefaultConfig defaultConfig)
(optionalAttrs (cfg.config != null) cfg.config);
configJSON = pkgs.writeText "configuration.json"
(builtins.toJSON (if cfg.applyDefaultConfig then
(recursiveUpdate defaultConfig cfg.config) else cfg.config));
(builtins.toJSON hassConfig);
configFile = pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } ''
${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out
# Hack to support secrets, that are encoded as custom yaml objects,
Expand Down Expand Up @@ -40,11 +42,11 @@ let
# platform = "mqtt";
# ...
# } ];
useComponentPlatform = component: elem component (usedPlatforms cfg.config);
useComponentPlatform = component: elem component (usedPlatforms hassConfig);

# Returns whether component is used in config
useComponent = component:
hasAttrByPath (splitString "." component) cfg.config
hasAttrByPath (splitString "." component) hassConfig
|| useComponentPlatform component;

# List of components used in config
Expand All @@ -56,8 +58,10 @@ let

# If you are changing this, please update the description in applyDefaultConfig
defaultConfig = {
homeassistant.time_zone = config.time.timeZone;
frontend = {};
http.server_port = cfg.port;
} // optionalAttrs (config.time.timeZone != null) {
homeassistant.time_zone = config.time.timeZone;
} // optionalAttrs (cfg.lovelaceConfig != null) {
lovelace.mode = "yaml";
};
Expand Down Expand Up @@ -87,10 +91,9 @@ in {
Setting this option enables a few configuration options for HA based on NixOS configuration (such as time zone) to avoid having to manually specify configuration we already have.
</para>
<para>
Currently one side effect of enabling this is that the <literal>http</literal> component will be enabled.
Currently one side effect of enabling this is that the <literal>http</literal> and <literal>frontend</literal> components will be enabled.
</para>
<para>
This only takes effect if <literal>config != null</literal> in order to ensure that a manually managed <filename>configuration.yaml</filename> is not overwritten.
'';
};

Expand Down Expand Up @@ -225,7 +228,7 @@ in {
systemd.services.home-assistant = {
description = "Home Assistant";
after = [ "network.target" ];
preStart = optionalString (cfg.config != null) (if cfg.configWritable then ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the actual bug fix is this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but I believe frontend missing was also causing issues. It's been quite a while though and it's not that fresh anymore. I'll have to spend some time going through and running it in various situations.

preStart = (if cfg.configWritable then ''
cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml"
'' else ''
rm -f "${cfg.configDir}/configuration.yaml"
Expand Down
65 changes: 65 additions & 0 deletions nixos/tests/home-assistant-config.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import ./make-test-python.nix ({ pkgs, ... }:

let
configDir = "/var/lib/hass";
configFile = "${configDir}/configuration.yaml";
port = 4000;

mkHass = { machineAttrs ? {}, hassAttrs ? {} }:
{ pkgs, ... }:
{
services.home-assistant = {
inherit configDir port;
enable = true;
} // hassAttrs;
} // machineAttrs;
in {
name = "home-assistant-config";

nodes = {
hassMinimal = mkHass {};

hassTimeZone = mkHass {
machineAttrs = { time.timeZone = "UTC"; };
};

hassNoDefault = mkHass {
hassAttrs = { applyDefaultConfig = false; };
};
Comment on lines +20 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we are going to need all those tests. They don't test much but still needs to be maintained and they are expensive to evaluate. It would be fine if they only would generate the configuration but they spawn actual VMs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Mic92 thanks for the feedback. This was my feeling as well, but I wasn't sure how to do unit tests in Nix. Perhaps I just missed some examples or documentation. Is there anything you could point me to that might help?

};

testScript = let
portStr = toString port;
in ''
start_all()


def check_availability(hass):
hass.wait_for_unit("home-assistant.service")
with subtest("Check that Home Assistant can be reached via port ${portStr}"):
hass.wait_for_open_port(${portStr})
hass.succeed("curl --fail http://localhost:${portStr}")


check_availability(hassMinimal)
with subtest("Check that port is set"):
config = hassMinimal.succeed("cat ${configFile}")
assert "server_port: ${portStr}" in config

check_availability(hassTimeZone)
with subtest("Check that default_config, port and time_zone are set"):
config = hassTimeZone.succeed("cat ${configFile}")
assert "server_port: ${portStr}" in config
assert "time_zone: UTC" in config

hassNoDefault.wait_for_unit("home-assistant.service")

with subtest("Check that server port is not set"):
config = hassNoDefault.succeed("cat ${configFile}")
assert "server_port: ${portStr}" not in config

# It cannot be reached because defaults (i.e. http.server_port, frontend) are not set
with subtest("Check that Home Assistant cannot be reached via port ${portStr}"):
hassNoDefault.wait_for_closed_port(${portStr})
'';
})