lol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

system.services: Remove ambiguous, redundant pkgs module argument

Primary reasons: remove implicit dependencies and force uniformity.
See nixos/modules/system/service/README.md for detailed rationale.

+174 -41
+5 -1
nixos/README-modular-services.md
··· 51 51 Provide it as the first attribute in the module: 52 52 53 53 ```nix 54 + # Non-module dependencies (`importApply`) 55 + { writeScript, runtimeShell }: 56 + 57 + # Service module 54 58 { lib, config, ... }: 55 59 { 56 60 _class = "service"; ··· 87 91 passthru = { 88 92 services = { 89 93 default = { 90 - imports = [ ./service.nix ]; 94 + imports = [ (lib.modules.importApply ./service.nix { inherit pkgs; }) ]; 91 95 example.package = finalAttrs.finalPackage; 92 96 # ... 93 97 };
+3 -1
nixos/doc/manual/development/modular-services.md
··· 85 85 86 86 ## Writing and Reviewing a Modular Service {#modular-service-review} 87 87 88 - Refer to the contributor documentation in [`nixos/README-modular-services.md`](https://github.com/NixOS/nixpkgs/blob/master/nixos/README-modular-services.md). 88 + A typical service module consists of the following: 89 + 90 + For more details, refer to the contributor documentation in [`nixos/README-modular-services.md`](https://github.com/NixOS/nixpkgs/blob/master/nixos/README-modular-services.md). 89 91 90 92 ## Portable Service Options {#modular-service-options-portable} 91 93
+90
nixos/modules/system/service/README.md
··· 53 53 54 54 - **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. 55 55 Per-service user creation is still TBD. 56 + 57 + ## No `pkgs` module argument 58 + 59 + The modular service infrastructure avoids exposing `pkgs` as a module argument to service modules. Instead, derivations and builder functions are provided through lexical closure, making dependency relationships explicit and avoiding uncertainty about where dependencies come from. 60 + 61 + ### Benefits 62 + 63 + - **Explicit dependencies**: Services declare what they need rather than implicitly depending on `pkgs` 64 + - **No interference**: Service modules can be reused in different contexts without assuming a specific `pkgs` instance. An unexpected `pkgs` version is not a failure mode anymore. 65 + - **Clarity**: With fewer ways to do things, there's no ambiguity about where dependencies come from (from the module, not the OS or service manager) 66 + 67 + ### Implementation 68 + 69 + - **Portable layer**: Service modules in `portable/` do not receive `pkgs` as a module argument. Any required derivations must be provided by the caller. 70 + 71 + - **Systemd integration**: The `systemd/system.nix` module imports `config-data.nix` as a function, providing `pkgs` in lexical closure: 72 + ```nix 73 + (import ../portable/config-data.nix { inherit pkgs; }) 74 + ``` 75 + 76 + - **Service modules**: 77 + 1. Should explicitly declare their package dependencies as options rather than using `pkgs` defaults: 78 + ```nix 79 + { 80 + # Bad: uses pkgs module argument 81 + foo.package = mkOption { 82 + default = pkgs.python3; 83 + # ... 84 + }; 85 + } 86 + ``` 87 + 88 + ```nix 89 + { 90 + # Good: caller provides the package 91 + foo.package = mkOption { 92 + type = types.package; 93 + description = "Python package to use"; 94 + defaultText = lib.literalMD "The package that provided this module."; 95 + }; 96 + } 97 + ``` 98 + 99 + 2. `passthru.services` can still provide a complete module using the package's lexical scope, making the module truly self-contained: 100 + 101 + **Package (`package.nix`):** 102 + ```nix 103 + { 104 + lib, 105 + writeScript, 106 + runtimeShell, 107 + # ... other dependencies 108 + }: 109 + stdenv.mkDerivation (finalAttrs: { 110 + # ... package definition 111 + 112 + passthru.services.default = { 113 + imports = [ 114 + (lib.modules.importApply ./service.nix { 115 + inherit writeScript runtimeShell; 116 + }) 117 + ]; 118 + someService.package = finalAttrs.finalPackage; 119 + }; 120 + }) 121 + ``` 122 + 123 + **Service module (`service.nix`):** 124 + ```nix 125 + # Non-module dependencies (importApply) 126 + { writeScript, runtimeShell }: 127 + 128 + # Service module 129 + { 130 + lib, 131 + config, 132 + options, 133 + ... 134 + }: 135 + { 136 + # Service definition using writeScript, runtimeShell from lexical scope 137 + process.argv = [ 138 + (writeScript "wrapper" '' 139 + #!${runtimeShell} 140 + # ... wrapper logic 141 + '') 142 + # ... other args 143 + ]; 144 + } 145 + ```
+4 -1
nixos/modules/system/service/portable/config-data.nix
··· 1 1 # Tests in: ../../../../tests/modular-service-etc/test.nix 2 + 3 + # Non-modular context provided by the modular services integration. 4 + { pkgs }: 5 + 2 6 # Configuration data support for portable services 3 7 # This module provides configData for services, enabling configuration reloading 4 8 # without terminating and restarting the service process. 5 9 { 6 10 lib, 7 - pkgs, 8 11 ... 9 12 }: 10 13 let
-1
nixos/modules/system/service/portable/service.nix
··· 11 11 _class = "service"; 12 12 imports = [ 13 13 ../../../misc/assertions.nix 14 - ./config-data.nix 15 14 ]; 16 15 options = { 17 16 services = mkOption {
+4 -7
nixos/modules/system/service/systemd/system.nix
··· 73 73 modules = [ 74 74 ./service.nix 75 75 ./config-data-path.nix 76 + (lib.modules.importApply ../portable/config-data.nix { inherit pkgs; }) 76 77 77 - # TODO: Consider removing pkgs. Service modules can provide their own 78 - # dependencies. 79 78 { 80 79 # Extend portable services option 81 80 options.services = lib.mkOption { 82 81 type = types.attrsOf ( 83 82 types.submoduleWith { 84 - specialArgs.pkgs = pkgs; 85 - modules = [ ]; 83 + modules = [ 84 + (lib.modules.importApply ../portable/config-data.nix { inherit pkgs; }) 85 + ]; 86 86 } 87 87 ); 88 88 }; ··· 90 90 ]; 91 91 specialArgs = { 92 92 # perhaps: features."systemd" = { }; 93 - # TODO: Consider removing pkgs. Service modules can provide their own 94 - # dependencies. 95 - inherit pkgs; 96 93 systemdPackage = config.systemd.package; 97 94 }; 98 95 }
+3 -17
nixos/tests/modular-service-etc/python-http-server.nix
··· 3 3 { 4 4 config, 5 5 lib, 6 - pkgs, 7 6 ... 8 7 }: 9 8 let ··· 16 15 python-http-server = { 17 16 package = mkOption { 18 17 type = types.package; 19 - default = pkgs.python3; 20 18 description = "Python package to use for the web server"; 21 19 }; 22 20 ··· 46 44 ]; 47 45 48 46 configData = { 49 - # This should probably just be {} if we were to put this module in production. 50 - "webroot" = lib.mkDefault { 51 - source = pkgs.runCommand "default-webroot" { } '' 52 - mkdir -p $out 53 - cat > $out/index.html << 'EOF' 54 - <!DOCTYPE html> 55 - <html> 56 - <head><title>Python Web Server</title></head> 57 - <body> 58 - <h1>Welcome to the Python Web Server</h1> 59 - <p>Serving from port ${toString config.python-http-server.port}</p> 60 - </body> 61 - </html> 62 - EOF 63 - ''; 47 + "webroot" = { 48 + # Enable only if directory is set to use this path 49 + enable = lib.mkDefault (config.python-http-server.directory == config.configData."webroot".path); 64 50 }; 65 51 }; 66 52 };
+28 -2
nixos/tests/modular-service-etc/test.nix
··· 12 12 nodes = { 13 13 server = 14 14 { pkgs, ... }: 15 + let 16 + # Normally the package services.default attribute combines this, but we 17 + # don't have that, because this is not a production service. Should it be? 18 + python-http-server = { 19 + imports = [ ./python-http-server.nix ]; 20 + python-http-server.package = pkgs.python3; 21 + }; 22 + in 15 23 { 16 24 system.services.webserver = { 17 25 # The python web server is simple enough that it doesn't need a reload signal. 18 26 # Other services may need to receive a signal in order to re-read what's in `configData`. 19 - imports = [ ./python-http-server.nix ]; 27 + imports = [ python-http-server ]; 20 28 python-http-server = { 21 29 port = 8080; 22 30 }; 23 31 32 + configData = { 33 + "webroot" = { 34 + source = pkgs.runCommand "webroot" { } '' 35 + mkdir -p $out 36 + cat > $out/index.html << 'EOF' 37 + <!DOCTYPE html> 38 + <html> 39 + <head><title>Python Web Server</title></head> 40 + <body> 41 + <h1>Welcome to the Python Web Server</h1> 42 + <p>Serving from port 8080</p> 43 + </body> 44 + </html> 45 + EOF 46 + ''; 47 + }; 48 + }; 49 + 24 50 # Add a sub-service 25 51 services.api = { 26 - imports = [ ./python-http-server.nix ]; 52 + imports = [ python-http-server ]; 27 53 python-http-server = { 28 54 port = 8081; 29 55 };
+2
nixos/tests/php/fpm-modular.nix
··· 1 + # Run with: 2 + # nix-build -A nixosTests.php.fpm-modular 1 3 { lib, php, ... }: 2 4 { 3 5 name = "php-${php.version}-fpm-modular-nginx-test";
+7 -1
pkgs/by-name/gh/ghostunnel/package.nix
··· 7 7 ghostunnel, 8 8 apple-sdk_12, 9 9 darwinMinVersionHook, 10 + writeScript, 11 + runtimeShell, 10 12 }: 11 13 12 14 buildGoModule rec { ··· 41 43 }; 42 44 43 45 passthru.services.default = { 44 - imports = [ ./service.nix ]; 46 + imports = [ 47 + (lib.modules.importApply ./service.nix { 48 + inherit writeScript runtimeShell; 49 + }) 50 + ]; 45 51 ghostunnel.package = ghostunnel; # FIXME: finalAttrs.finalPackage 46 52 }; 47 53
+7 -3
pkgs/by-name/gh/ghostunnel/service.nix
··· 1 + # Non-module dependencies (`importApply`) 2 + { writeScript, runtimeShell }: 3 + 4 + # Service module 1 5 { 2 6 lib, 3 7 config, 4 8 options, 5 - pkgs, 6 9 ... 7 10 }: 8 11 let ··· 25 28 ghostunnel = { 26 29 package = mkOption { 27 30 description = "Package to use for ghostunnel"; 31 + defaultText = "The ghostunnel package that provided this module."; 28 32 type = types.package; 29 33 }; 30 34 ··· 191 195 cfg.cacert 192 196 ]) 193 197 ( 194 - pkgs.writeScript "load-credentials" '' 195 - #!${pkgs.runtimeShell} 198 + writeScript "load-credentials" '' 199 + #!${runtimeShell} 196 200 exec $@ ${ 197 201 concatStringsSep " " ( 198 202 optional (cfg.keystore != null) "--keystore=$CREDENTIALS_DIRECTORY/keystore"
+7 -1
pkgs/development/interpreters/php/generic.nix
··· 32 32 common-updater-scripts, 33 33 curl, 34 34 jq, 35 + coreutils, 36 + formats, 35 37 36 38 version, 37 39 phpSrc ? null, ··· 390 392 inherit ztsSupport; 391 393 392 394 services.default = { 393 - imports = [ ./service.nix ]; 395 + imports = [ 396 + (lib.modules.importApply ./service.nix { 397 + inherit formats coreutils; 398 + }) 399 + ]; 394 400 php-fpm.package = lib.mkDefault finalAttrs.finalPackage; 395 401 }; 396 402 };
+14 -6
pkgs/development/interpreters/php/service.nix
··· 1 + # Tests in: nixos/tests/php/fpm-modular.nix 2 + 3 + # Non-module dependencies (importApply) 4 + { formats, coreutils }: 5 + 6 + # Service module 1 7 { 2 8 options, 3 9 config, 4 - pkgs, 5 10 lib, 6 11 ... 7 12 }: 8 13 let 9 14 cfg = config.php-fpm; 10 - format = pkgs.formats.iniWithGlobalSection { }; 15 + format = formats.iniWithGlobalSection { }; 11 16 configFile = format.generate "php-fpm.conf" { 12 17 globalSection = lib.filterAttrs (_: v: !lib.isAttrs v) cfg.settings; 13 18 sections = lib.filterAttrs (_: lib.isAttrs) cfg.settings; ··· 76 81 _class = "service"; 77 82 78 83 options.php-fpm = { 79 - package = lib.mkPackageOption pkgs "php" { 80 - example = '' 84 + package = lib.mkOption { 85 + type = lib.types.package; 86 + description = "PHP package to use for php-fpm"; 87 + defaultText = lib.literalMD ''The PHP package that provided this module.''; 88 + example = lib.literalExpression '' 81 89 php.buildEnv { 82 90 extensions = 83 91 { all, ... }: ··· 163 171 164 172 serviceConfig = { 165 173 Type = "notify"; 166 - ExecReload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; 174 + ExecReload = "${coreutils}/bin/kill -USR2 $MAINPID"; 167 175 RuntimeDirectory = "php-fpm"; 168 176 RuntimeDirectoryPreserve = true; 169 177 Restart = "always"; ··· 175 183 176 184 finit.service = { 177 185 conditions = [ "service/syslogd/ready" ]; 178 - reload = "${pkgs.coreutils}/bin/kill -USR2 $MAINPID"; 186 + reload = "${coreutils}/bin/kill -USR2 $MAINPID"; 179 187 notify = "systemd"; 180 188 }; 181 189 };