+27
nixos/modules/system/service/README.md
+27
nixos/modules/system/service/README.md
···- `systemServices`: similar to does not allow importing a composition of services into `system`. Not sure if that's a good idea in the first place, but I've kept the possibility open.- `services.abstract`: used in https://github.com/NixOS/nixpkgs/pull/267111, but too weird. Service modules should fit naturally into the configuration system.···- This reserves `modules/service` for actual service modules, at least until those are lifted out of NixOS, potentially+Without a mechanism for adding files, all configuration had to go through `process.*`, requiring process restarts even when those would have been avoidable.+Many services implement automatic reloading or reloading on e.g. `SIGUSR1`, but those mechanisms need files to read. `configData` provides such files.+- **`configData` instead of `environment.etc`**: The name `configData` is service manager agnostic. While systemd system services can use `/etc`, other service managers may expose configuration data differently (e.g., different directory, relative paths).+- **`path` attribute**: Each `configData` entry automatically gets a `path` attribute set by the service manager implementation, allowing services to reference the location of their configuration files. These paths themselves are not subject to change from generation to generation; only their contents are.+- **`name` attribute**: In `environment.etc` this would be `target` but that's confusing, especially for symlinks, as it's not the symlink's target.+- **Portable base**: The `configData` interface is declared in `portable/config-data.nix`, making it available to all service manager implementations.+- **Systemd integration**: The systemd implementation (`systemd/system.nix`) maps `configData` entries to `environment.etc` entries under `/etc/system-services/`.+- **Path computation**: `systemd/config-data-path.nix` recursively computes unique paths for services and sub-services (e.g., `/etc/system-services/webserver/` vs `/etc/system-services/webserver-api/`).+Fun fact: for the module system it is a completely normal module, despite its recursive definition.+If we parameterize `/etc/system-services`, it will have to become an `importApply` style module nonetheless (function returning module).+- **Simple attribute structure**: Unlike `environment.etc`, `configData` uses a simpler structure with just `enable`, `name`, `text`, `source`, and `path` attributes. Complex ownership options were omitted for simplicity and portability.
+65
nixos/modules/system/service/portable/config-data-item.nix
+65
nixos/modules/system/service/portable/config-data-item.nix
···+Name of the configuration file (relative to the service's configuration directory). Defaults to the attribute name.+Other service managers may provide a relative path, in order to be unprivileged and/or relocatable.
+44
nixos/modules/system/service/portable/config-data.nix
+44
nixos/modules/system/service/portable/config-data.nix
···+These files are made available to the service and can be updated without restarting the service process, enabling configuration reloading.+The service manager implementation determines how these files are exposed to the service (e.g., via a specific directory path).+This is particularly useful for services that support configuration reloading via signals (e.g., SIGHUP) or which pick up changes automatically, so that no downtime is required in order to reload the service.
+1
nixos/modules/system/service/portable/service.nix
+1
nixos/modules/system/service/portable/service.nix
+39
nixos/modules/system/service/systemd/config-data-path.nix
+39
nixos/modules/system/service/systemd/config-data-path.nix
···
+26
nixos/modules/system/service/systemd/system.nix
+26
nixos/modules/system/service/systemd/system.nix
·········
+1
nixos/tests/all-tests.nix
+1
nixos/tests/all-tests.nix
···
+67
nixos/tests/modular-service-etc/python-http-server.nix
+67
nixos/tests/modular-service-etc/python-http-server.nix
···
+181
nixos/tests/modular-service-etc/test.nix
+181
nixos/tests/modular-service-etc/test.nix
···+client.succeed("curl -f http://server:8080/index.html | grep 'Welcome to the Python Web Server'")+client.succeed("curl -f http://server:8081/index.html | grep 'This is a sub-service running on port 8081'")+webserver_pid = server.succeed("systemctl show webserver.service --property=MainPID --value").strip()+api_pid = server.succeed("systemctl show webserver-api.service --property=MainPID --value").strip()+switch_output = server.succeed("/run/current-system/specialisation/updated/bin/switch-to-configuration test")+assert "webserver.service" not in switch_output, f"webserver.service was mentioned in switch output: {switch_output}"+assert "webserver-api.service" not in switch_output, f"webserver-api.service was mentioned in switch output: {switch_output}"+webserver_pid_after = server.succeed("systemctl show webserver.service --property=MainPID --value").strip()+api_pid_after = server.succeed("systemctl show webserver-api.service --property=MainPID --value").strip()+assert webserver_pid == webserver_pid_after, f"webserver.service was restarted: PID changed from {webserver_pid} to {webserver_pid_after}"+assert api_pid == api_pid_after, f"webserver-api.service was restarted: PID changed from {api_pid} to {api_pid_after}"+client.succeed("curl -f http://server:8080/index.html | grep 'Updated content via specialisation'")+client.succeed("curl -f http://server:8080/index.html | grep 'This content was changed without restarting the service'")+client.succeed("curl -f http://server:8081/index.html | grep 'This sub-service content was also updated'")