Project import generated by Copybara.

GitOrigin-RevId: 395879c28386e1abf20c7ecacd45880759548391
This commit is contained in:
Default email 2021-12-18 20:06:50 -05:00
parent 576896970a
commit 3cbfd2b52c
1939 changed files with 41662 additions and 38968 deletions

View file

@ -39,7 +39,7 @@
/pkgs/top-level/stage.nix @nbp @Ericson2314 @matthewbauer
/pkgs/top-level/splice.nix @Ericson2314 @matthewbauer
/pkgs/top-level/release-cross.nix @Ericson2314 @matthewbauer
/pkgs/stdenv/generic @Ericson2314 @matthewbauer
/pkgs/stdenv/generic @Ericson2314 @matthewbauer @cab404
/pkgs/stdenv/cross @Ericson2314 @matthewbauer
/pkgs/build-support/cc-wrapper @Ericson2314 @orivej
/pkgs/build-support/bintools-wrapper @Ericson2314 @orivej
@ -219,9 +219,9 @@
# Podman, CRI-O modules and related
/nixos/modules/virtualisation/containers.nix @NixOS/podman @zowoq @adisbladis
/nixos/modules/virtualisation/cri-o.nix @NixOS/podman @zowoq @adisbladis
/nixos/modules/virtualisation/podman.nix @NixOS/podman @zowoq @adisbladis
/nixos/modules/virtualisation/podman @NixOS/podman @zowoq @adisbladis
/nixos/tests/cri-o.nix @NixOS/podman @zowoq @adisbladis
/nixos/tests/podman.nix @NixOS/podman @zowoq @adisbladis
/nixos/tests/podman @NixOS/podman @zowoq @adisbladis
# Docker tools
/pkgs/build-support/docker @roberth @utdemir

View file

@ -31,7 +31,8 @@ jobs:
- uses: cachix/install-nix-action@v16
with:
# nixpkgs commit is pinned so that it doesn't break
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/f93ecc4f6bc60414d8b73dbdf615ceb6a2c604df.tar.gz
# editorconfig-checker 2.4.0
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/c473cc8714710179df205b153f4e9fa007107ff9.tar.gz
- name: install editorconfig-checker
run: nix-env -iA editorconfig-checker -f '<nixpkgs>'
- name: Checking EditorConfig

View file

@ -72,7 +72,18 @@ The `dotnetCorePackages.sdk` contains both a runtime and the full sdk of a given
To package Dotnet applications, you can use `buildDotnetModule`. This has similar arguments to `stdenv.mkDerivation`, with the following additions:
* `projectFile` has to be used for specifying the dotnet project file relative to the source root. These usually have `.sln` or `.csproj` file extensions. This can be an array of multiple projects as well.
* `nugetDeps` has to be used to specify the NuGet dependency file. Unfortunately, these cannot be deterministically fetched without a lockfile. This file should be generated using `nuget-to-nix` tool, which is available in nixpkgs.
* `nugetDeps` has to be used to specify the NuGet dependency file. Unfortunately, these cannot be deterministically fetched without a lockfile. A script to fetch these is available as `passthru.fetch-deps`. This file can also be generated manually using `nuget-to-nix` tool, which is available in nixpkgs.
* `packNupkg` is used to pack project as a `nupkg`, and installs it to `$out/share`. If set to `true`, the derivation can be used as a dependency for another dotnet project by adding it to `projectReferences`.
* `projectReferences` can be used to resolve `ProjectReference` project items. Referenced projects can be packed with `buildDotnetModule` by setting the `packNupkg = true` attribute and passing a list of derivations to `projectReferences`. Since we are sharing referenced projects as NuGets they must be added to csproj/fsproj files as `PackageReference` as well.
For example, your project has a local dependency:
```xml
<ProjectReference Include="../foo/bar.fsproj" />
```
To enable discovery through `projectReferences` you would need to add:
```xml
<ProjectReference Include="../foo/bar.fsproj" />
<PackageReference Include="bar" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
```
* `executables` is used to specify which executables get wrapped to `$out/bin`, relative to `$out/lib/$pname`. If this is unset, all executables generated will get installed. If you do not want to install any, set this to `[]`.
* `runtimeDeps` is used to wrap libraries into `LD_LIBRARY_PATH`. This is how dotnet usually handles runtime dependencies.
* `buildType` is used to change the type of build. Possible values are `Release`, `Debug`, etc. By default, this is set to `Release`.
@ -83,22 +94,29 @@ To package Dotnet applications, you can use `buildDotnetModule`. This has simila
* `disabledTests` is used to disable running specific unit tests. This gets passed as: `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all unit test frameworks.
* `dotnetRestoreFlags` can be used to pass flags to `dotnet restore`.
* `dotnetBuildFlags` can be used to pass flags to `dotnet build`.
* `dotnetTestFlags` can be used to pass flags to `dotnet test`.
* `dotnetTestFlags` can be used to pass flags to `dotnet test`. Used only if `doCheck` is set to `true`.
* `dotnetInstallFlags` can be used to pass flags to `dotnet install`.
* `dotnetPackFlags` can be used to pass flags to `dotnet pack`. Used only if `packNupkg` is set to `true`.
* `dotnetFlags` can be used to pass flags to all of the above phases.
When packaging a new application, you need to fetch it's dependencies. You can set `nugetDeps` to an empty string to make the derivation temporarily evaluate, and then run `nix-build -A package.passthru.fetch-deps` to generate it's dependency fetching script. After running the script, you should have the location of the generated lockfile printed to the console. This can be copied to a stable directory. Note that if either `projectFile` or `nugetDeps` are unset, this script cannot be generated!
Here is an example `default.nix`, using some of the previously discussed arguments:
```nix
{ lib, buildDotnetModule, dotnetCorePackages, ffmpeg }:
buildDotnetModule rec {
let
referencedProject = import ../../bar { ... };
in buildDotnetModule rec {
pname = "someDotnetApplication";
version = "0.1";
src = ./.;
projectFile = "src/project.sln";
nugetDeps = ./deps.nix; # File generated with `nuget-to-nix path/to/src > deps.nix`.
nugetDeps = ./deps.nix; # File generated with `nix-build -A package.passthru.fetch-deps`.
projectReferences = [ referencedProject ]; # `referencedProject` must contain `nupkg` in the folder structure.
dotnet-sdk = dotnetCorePackages.sdk_3_1;
dotnet-runtime = dotnetCorePackages.net_5_0;
@ -107,6 +125,8 @@ buildDotnetModule rec {
executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`.
executables = []; # Don't install any executables.
packNupkg = true; # This packs the project as "foo-0.1.nupkg" at `$out/share`.
runtimeDeps = [ ffmpeg ]; # This will wrap ffmpeg's library path into `LD_LIBRARY_PATH`.
}
```

View file

@ -92,7 +92,7 @@ For convenience, it also adds `dconf.lib` for a GIO module implementing a GSetti
- []{#ssec-gnome-hooks-glib} `glib` setup hook will populate `GSETTINGS_SCHEMAS_PATH` and then `wrapGAppsHook` will prepend it to `XDG_DATA_DIRS`.
- []{#ssec-gnome-hooks-gdk-pixbuf} `gdk-pixbuf` setup hook will populate `GDK_PIXBUF_MODULE_FILE` with the path to biggest `loaders.cache` file from the dependencies containing [GdkPixbuf loaders](ssec-gnome-gdk-pixbuf-loaders). This works fine when there are only two packages containing loaders (`gdk-pixbuf` and e.g. `librsvg`) it will choose the second one, reasonably expecting that it will be bigger since it describes extra loader in addition to the default ones. But when there are more than two loader packages, this logic will break. One possible solution would be constructing a custom cache file for each package containing a program like `services/x11/gdk-pixbuf.nix` NixOS module does. `wrapGAppsHook` copies the `GDK_PIXBUF_MODULE_FILE` environment variable into the produced wrapper.
- []{#ssec-gnome-hooks-gdk-pixbuf} `gdk-pixbuf` setup hook will populate `GDK_PIXBUF_MODULE_FILE` with the path to biggest `loaders.cache` file from the dependencies containing [GdkPixbuf loaders](#ssec-gnome-gdk-pixbuf-loaders). This works fine when there are only two packages containing loaders (`gdk-pixbuf` and e.g. `librsvg`) it will choose the second one, reasonably expecting that it will be bigger since it describes extra loader in addition to the default ones. But when there are more than two loader packages, this logic will break. One possible solution would be constructing a custom cache file for each package containing a program like `services/x11/gdk-pixbuf.nix` NixOS module does. `wrapGAppsHook` copies the `GDK_PIXBUF_MODULE_FILE` environment variable into the produced wrapper.
- []{#ssec-gnome-hooks-gtk-drop-icon-theme-cache} One of `gtk3`s setup hooks will remove `icon-theme.cache` files from packages icon theme directories to avoid conflicts. Icon theme packages should prevent this with `dontDropIconThemeCache = true;`.

View file

@ -128,7 +128,7 @@ Let's present the luarocks way first and the manual one in a second time.
### Packaging a library on luarocks {#packaging-a-library-on-luarocks}
[Luarocks.org](www.luarocks.org) is the main repository of lua packages.
[Luarocks.org](https://luarocks.org/) is the main repository of lua packages.
The site proposes two types of packages, the rockspec and the src.rock
(equivalent of a [rockspec](https://github.com/luarocks/luarocks/wiki/Rockspec-format) but with the source).
These packages can have different build types such as `cmake`, `builtin` etc .

View file

@ -995,18 +995,18 @@ called with `callPackage` and passed `python` or `pythonPackages` (possibly
specifying an interpreter version), like this:
```nix
{ lib, python3Packages }:
{ lib, python3 }:
python3Packages.buildPythonApplication rec {
python3.pkgs.buildPythonApplication rec {
pname = "luigi";
version = "2.7.9";
src = python3Packages.fetchPypi {
src = python3.pkgs.fetchPypi {
inherit pname version;
sha256 = "035w8gqql36zlan0xjrzz9j4lh9hs0qrsgnbyw07qs7lnkvbdv9x";
};
propagatedBuildInputs = with python3Packages; [ tornado_4 python-daemon ];
propagatedBuildInputs = with python3.pkgs; [ tornado python-daemon ];
meta = with lib; {
...
@ -1632,3 +1632,25 @@ would be:
```ShellSession
$ maintainers/scripts/update-python-libraries --target minor --commit --use-pkgs-prefix pkgs/development/python-modules/**/default.nix
```
## CPython Update Schedule
With [PEP 602](https://www.python.org/dev/peps/pep-0602/), CPython now
follows a yearly release cadence. In nixpkgs, all supported interpreters
are made available, but only the most recent two
interpreters package sets are built; this is a compromise between being
the latest interpreter, and what the majority of the Python packages support.
New CPython interpreters are released in October. Generally, it takes some
time for the majority of active Python projects to support the latest stable
interpreter. To help ease the migration for Nixpkgs users
between Python interpreters the schedule below will be used:
| When | Event |
| --- | --- |
| After YY.11 Release | Bump CPython package set window. The latest and previous latest stable should now be built. |
| After YY.05 Release | Bump default CPython interpreter to latest stable. |
In practice, this means that the Python community will have had a stable interpreter
for ~2 months before attempting to update the package set. And this will
allow for ~7 months for Python applications to support the latest interpreter.

View file

@ -201,6 +201,19 @@ $ nix-shell --run 'ruby -rpg -e "puts PG.library_version"'
Of course for this use-case one could also use overlays since the configuration for `pg` depends on the `postgresql` alias, but for demonstration purposes this has to suffice.
### Platform-specific gems
Right now, bundix has some issues with pre-built, platform-specific gems: [bundix PR #68](https://github.com/nix-community/bundix/pull/68).
Until this is solved, you can tell bundler to not use platform-specific gems and instead build them from source each time:
- globally (will be set in `~/.config/.bundle/config`):
```shell
$ bundle config set force_ruby_platform true
```
- locally (will be set in `<project-root>/.bundle/config`):
```shell
$ bundle config set --local force_ruby_platform true
```
### Adding a gem to the default gemset {#adding-a-gem-to-the-default-gemset}
Now that you know how to get a working Ruby environment with Nix, it's time to go forward and start actually developing with Ruby. We will first have a look at how Ruby gems are packaged on Nix. Then, we will look at how you can use development mode with your code.

View file

@ -293,7 +293,7 @@ Test flags, e.g., `--package foo`, can be passed to `cargo test` via the
Another attribute, called `checkFlags`, is used to pass arguments to the test
binary itself, as stated
(here)[https://doc.rust-lang.org/cargo/commands/cargo-test.html].
[here](https://doc.rust-lang.org/cargo/commands/cargo-test.html).
#### Tests relying on the structure of the `target/` directory {#tests-relying-on-the-structure-of-the-target-directory}

View file

@ -309,9 +309,9 @@ Sample output2:
## Adding new plugins to nixpkgs {#adding-new-plugins-to-nixpkgs}
Nix expressions for Vim plugins are stored in [pkgs/misc/vim-plugins](/pkgs/misc/vim-plugins). For the vast majority of plugins, Nix expressions are automatically generated by running [`./update.py`](/pkgs/misc/vim-plugins/update.py). This creates a [generated.nix](/pkgs/misc/vim-plugins/generated.nix) file based on the plugins listed in [vim-plugin-names](/pkgs/misc/vim-plugins/vim-plugin-names). Plugins are listed in alphabetical order in `vim-plugin-names` using the format `[github username]/[repository]@[gitref]`. For example https://github.com/scrooloose/nerdtree becomes `scrooloose/nerdtree`.
Nix expressions for Vim plugins are stored in [pkgs/misc/vim-plugins](https://github.com/NixOS/nixpkgs/tree/master/pkgs/misc/vim-plugins). For the vast majority of plugins, Nix expressions are automatically generated by running [`./update.py`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vim-plugins/update.py). This creates a [generated.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vim-plugins/generated.nix) file based on the plugins listed in [vim-plugin-names](https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vim-plugins/vim-plugin-names). Plugins are listed in alphabetical order in `vim-plugin-names` using the format `[github username]/[repository]@[gitref]`. For example https://github.com/scrooloose/nerdtree becomes `scrooloose/nerdtree`.
Some plugins require overrides in order to function properly. Overrides are placed in [overrides.nix](/pkgs/misc/vim-plugins/overrides.nix). Overrides are most often required when a plugin requires some dependencies, or extra steps are required during the build process. For example `deoplete-fish` requires both `deoplete-nvim` and `vim-fish`, and so the following override was added:
Some plugins require overrides in order to function properly. Overrides are placed in [overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vim-plugins/overrides.nix). Overrides are most often required when a plugin requires some dependencies, or extra steps are required during the build process. For example `deoplete-fish` requires both `deoplete-nvim` and `vim-fish`, and so the following override was added:
```nix
deoplete-fish = super.deoplete-fish.overrideAttrs(old: {

View file

@ -796,7 +796,7 @@ The standard environment provides a number of useful functions.
### `makeWrapper` \<executable\> \<wrapperfile\> \<args\> {#fun-makeWrapper}
Constructs a wrapper for a program with various possible arguments. For example:
Constructs a wrapper for a program with various possible arguments. It is defined as part of 2 setup-hooks named `makeWrapper` and `makeBinaryWrapper` that implement the same bash functions. Hence, to use it you have to add `makeWrapper` to your `nativeBuildInputs`. Here's an example usage:
```bash
# adds `FOOBAR=baz` to `$out/bin/foo`s environment
@ -808,9 +808,11 @@ makeWrapper $out/bin/foo $wrapperfile --set FOOBAR baz
makeWrapper $out/bin/foo $wrapperfile --prefix PATH : ${lib.makeBinPath [ hello git ]}
```
Theres many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh`.
Theres many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/build-support/setup-hooks/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
`wrapProgram` is a convenience function you probably want to use most of the time.
`wrapProgram` is a convenience function you probably want to use most of the time, implemented by both `makeWrapper` and `makeBinaryWrapper`.
Using the `makeBinaryWrapper` implementation is usually preferred, as it creates a tiny _compiled_ wrapper executable, that can be used as a shebang interpreter. This is needed mostly on Darwin, where shebangs cannot point to scripts, [due to a limitation with the `execve`-syscall](https://stackoverflow.com/questions/67100831/macos-shebang-with-absolute-path-not-working). Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less <path-to-wrapper>` - by scrolling past the binary data you should be able to see the shell command that generated the executable and there see the environment variables that were injected into the wrapper.
### `substitute` \<infile\> \<outfile\> \<subs\> {#fun-substitute}
@ -885,9 +887,9 @@ someVar=$(stripHash $name)
### `wrapProgram` \<executable\> \<makeWrapperArgs\> {#fun-wrapProgram}
Convenience function for `makeWrapper` that automatically creates a sane wrapper file. It takes all the same arguments as `makeWrapper`, except for `--argv0`.
Convenience function for `makeWrapper` that replaces `<\executable\>` with a wrapper that executes the original program. It takes all the same arguments as `makeWrapper`, except for `--inherit-argv0` (used by the `makeBinaryWrapper` implementation) and `--argv0` (used by both `makeWrapper` and `makeBinaryWrapper` wrapper implementations).
It cannot be applied multiple times, since it will overwrite the wrapper file.
If you will apply it multiple times, it will overwrite the wrapper file and you will end up with double wrapping, which should be avoided.
## Package setup hooks {#ssec-setup-hooks}

View file

@ -105,7 +105,7 @@ let
makeScope makeScopeWithSplicing;
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
hiPrioSet;
hiPrioSet getLicenseFromSpdxId;
inherit (self.sources) pathType pathIsDirectory cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext

View file

@ -99,4 +99,31 @@ rec {
availableOn = platform: pkg:
lib.any (platformMatch platform) pkg.meta.platforms &&
lib.all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
/* Get the corresponding attribute in lib.licenses
from the SPDX ID.
For SPDX IDs, see
https://spdx.org/licenses
Type:
getLicenseFromSpdxId :: str -> AttrSet
Example:
lib.getLicenseFromSpdxId "MIT" == lib.licenses.mit
=> true
lib.getLicenseFromSpdxId "mIt" == lib.licenses.mit
=> true
lib.getLicenseFromSpdxId "MY LICENSE"
=> trace: warning: getLicenseFromSpdxId: No license matches the given SPDX ID: MY LICENSE
=> { shortName = "MY LICENSE"; }
*/
getLicenseFromSpdxId =
let
spdxLicenses = lib.mapAttrs (id: ls: assert lib.length ls == 1; builtins.head ls)
(lib.groupBy (l: lib.toLower l.spdxId) (lib.filter (l: l ? spdxId) (lib.attrValues lib.licenses)));
in licstr:
spdxLicenses.${ lib.toLower licstr } or (
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
{ shortName = licstr; }
);
}

View file

@ -13,8 +13,6 @@ let
elem
filter
findFirst
flip
foldl
foldl'
getAttrFromPath
head
@ -101,9 +99,31 @@ rec {
check ? true
}:
let
withWarnings = x:
lib.warnIf (evalModulesArgs?args) "The args argument to evalModules is deprecated. Please set config._module.args instead."
lib.warnIf (evalModulesArgs?check) "The check argument to evalModules is deprecated. Please set config._module.check instead."
x;
legacyModules =
optional (evalModulesArgs?args) {
config = {
_module.args = args;
};
}
++ optional (evalModulesArgs?check) {
config = {
_module.check = mkDefault check;
};
};
regularModules = modules ++ legacyModules;
# This internal module declare internal options under the `_module'
# attribute. These options are fragile, as they are used by the
# module system to change the interpretation of modules.
#
# When extended with extendModules or moduleType, a fresh instance of
# this module is used, to avoid conflicts and allow chaining of
# extendModules.
internalModule = rec {
_file = ./modules.nix;
@ -125,7 +145,7 @@ rec {
_module.check = mkOption {
type = types.bool;
internal = true;
default = check;
default = true;
description = "Whether to check whether all option definitions have matching declarations.";
};
@ -151,14 +171,14 @@ rec {
_module.args = {
inherit extendModules;
moduleType = type;
} // args;
};
};
};
merged =
let collected = collectModules
(specialArgs.modulesPath or "")
(modules ++ [ internalModule ])
(regularModules ++ [ internalModule ])
({ inherit lib options config specialArgs; } // specialArgs);
in mergeModules prefix (reverseList collected);
@ -222,7 +242,7 @@ rec {
prefix ? [],
}:
evalModules (evalModulesArgs // {
modules = evalModulesArgs.modules ++ modules;
modules = regularModules ++ modules;
specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
prefix = extendArgs.prefix or evalModulesArgs.prefix;
});
@ -231,7 +251,7 @@ rec {
inherit modules specialArgs;
};
result = {
result = withWarnings {
options = checked options;
config = checked (removeAttrs config [ "_module" ]);
_module = checked (config._module);
@ -452,7 +472,7 @@ rec {
[{ inherit (module) file; inherit value; }]
) configs;
resultsByName = flip mapAttrs declsByName (name: decls:
resultsByName = mapAttrs (name: decls:
# We're descending into attribute name.
let
loc = prefix ++ [name];
@ -473,7 +493,7 @@ rec {
in
throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'."
else
mergeModules' loc decls defns);
mergeModules' loc decls defns) declsByName;
matchedOptions = mapAttrs (n: v: v.matchedOptions) resultsByName;
@ -487,12 +507,19 @@ rec {
inherit matchedOptions;
# Transforms unmatchedDefnsByName into a list of definitions
unmatchedDefns = concatLists (mapAttrsToList (name: defs:
map (def: def // {
# Set this so we know when the definition first left unmatched territory
prefix = [name] ++ (def.prefix or []);
}) defs
) unmatchedDefnsByName);
unmatchedDefns =
if configs == []
then
# When no config values exist, there can be no unmatched config, so
# we short circuit and avoid evaluating more _options_ than necessary.
[]
else
concatLists (mapAttrsToList (name: defs:
map (def: def // {
# Set this so we know when the definition first left unmatched territory
prefix = [name] ++ (def.prefix or []);
}) defs
) unmatchedDefnsByName);
};
/* Merge multiple option declarations into a single declaration. In
@ -587,6 +614,8 @@ rec {
definitions = map (def: def.value) res.defsFinal;
files = map (def: def.file) res.defsFinal;
inherit (res) isDefined;
# This allows options to be correctly displayed using `${options.path.to.it}`
__toString = _: showOption loc;
};
# Merge definitions of a value of a given type.
@ -906,7 +935,7 @@ rec {
mkMergedOptionModule = from: to: mergeFn:
{ config, options, ... }:
{
options = foldl recursiveUpdate {} (map (path: setAttrByPath path (mkOption {
options = foldl' recursiveUpdate {} (map (path: setAttrByPath path (mkOption {
visible = false;
# To use the value in mergeFn without triggering errors
default = "_mkMergedOptionModule";
@ -1010,7 +1039,7 @@ rec {
/* Use this function to import a JSON file as NixOS configuration.
importJSON -> path -> attrs
modules.importJSON :: path -> attrs
*/
importJSON = file: {
_file = file;
@ -1019,7 +1048,7 @@ rec {
/* Use this function to import a TOML file as NixOS configuration.
importTOML -> path -> attrs
modules.importTOML :: path -> attrs
*/
importTOML = file: {
_file = file;

View file

@ -39,8 +39,8 @@ let
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
# none
"aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
"msp430-none" "or1k-none" "m68k-none" "powerpc-none"
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
"msp430-none" "or1k-none" "m68k-none" "powerpc-none" "powerpcle-none"
"riscv32-none" "riscv64-none" "s390-none" "s390x-none" "vc4-none"
"x86_64-none"

View file

@ -290,6 +290,7 @@ rec {
libc = "nblibc";
};
# this is broken and never worked fully
x86_64-netbsd-llvm = {
config = "x86_64-unknown-netbsd";
libc = "nblibc";

View file

@ -4,7 +4,9 @@
{ lib }:
rec {
# List of systems that are built by Hydra.
hydra = tier1 ++ tier2 ++ tier3;
hydra = tier1 ++ tier2 ++ tier3 ++ [
"aarch64-darwin"
];
tier1 = [
"x86_64-linux"
@ -16,7 +18,6 @@ rec {
];
tier3 = [
"aarch64-darwin"
"armv6l-linux"
"armv7l-linux"
"i686-linux"

View file

@ -496,7 +496,7 @@ runTests {
testToPretty =
let
deriv = derivation { name = "test"; builder = "/bin/sh"; system = builtins.currentSystem; };
deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
in {
expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
int = 42;

View file

@ -1,6 +1,13 @@
{ lib, ... }: {
{ lib, ... }:
let
deathtrapArgs = lib.mapAttrs
(k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.")
(lib.functionArgs lib.mkOption);
in
{
options.value = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
};
options.testing-laziness-so-don't-read-me = lib.mkOption deathtrapArgs;
}

View file

@ -1,7 +1,14 @@
{ lib, ... }: {
{ lib, ... }:
let
deathtrapArgs = lib.mapAttrs
(k: _: throw "The module system is too strict, accessing an unused option's ${k} mkOption-attribute.")
(lib.functionArgs lib.mkOption);
in
{
options.nest.foo = lib.mkOption {
type = lib.types.bool;
default = false;
};
options.nest.unused = lib.mkOption deathtrapArgs;
config.nest.bar = "bar";
}

View file

@ -278,6 +278,12 @@
githubId = 1250775;
name = "Adolfo E. García Castro";
};
AdsonCicilioti = {
name = "Adson Cicilioti";
email = "adson.cicilioti@live.com";
github = "AdsonCicilioti";
githubId = 6278398;
};
adsr = {
email = "as@php.net";
github = "adsr";
@ -351,10 +357,10 @@
name = "AmirHossein Roozbahani";
};
ahuzik = {
email = "ales.guzik@gmail.com";
github = "alesguzik";
email = "ah1990au@gmail.com";
github = "alesya-h";
githubId = 209175;
name = "Ales Huzik";
name = "Alesya Huzik";
};
aij = {
email = "aij+git@mrph.org";
@ -1408,6 +1414,12 @@
githubId = 251106;
name = "Daniel Bergey";
};
bergkvist = {
email = "tobias@bergkv.ist";
github = "bergkvist";
githubId = 410028;
name = "Tobias Bergkvist";
};
betaboon = {
email = "betaboon@0x80.ninja";
github = "betaboon";
@ -3683,6 +3695,7 @@
};
evils = {
email = "evils.devils@protonmail.com";
matrix = "@evils:nixos.dev";
github = "evils";
githubId = 30512529;
name = "Evils";
@ -4856,6 +4869,16 @@
githubId = 12491746;
name = "Masato Yonekawa";
};
hyshka = {
name = "Bryan Hyshka";
email = "bryan@hyshka.com";
github = "hyshka";
githubId = 2090758;
keys = [{
longkeyid = "rsa2048/0xDB2D93D1BFAAA6EA";
fingerprint = "24F4 1925 28C4 8797 E539 F247 DB2D 93D1 BFAA A6EA";
}];
};
hyzual = {
email = "hyzual@gmail.com";
github = "Hyzual";
@ -7665,6 +7688,12 @@
githubId = 21156022;
name = "Michal Minář";
};
michzappa = {
email = "me@michzappa.com";
github = "michzappa";
githubId = 59343378;
name = "Michael Zappa";
};
mickours = {
email = "mickours@gmail.com<";
github = "mickours";
@ -9015,6 +9044,12 @@
githubId = 116740;
name = "Paweł Pacana";
};
pb- = {
email = "pbaecher@gmail.com";
github = "pb-";
githubId = 84886;
name = "Paul Baecher";
};
pbogdan = {
email = "ppbogdan@gmail.com";
github = "pbogdan";
@ -12095,6 +12130,12 @@
githubId = 122319;
name = "Renato Alves";
};
unrooted = {
name = "Konrad Klawikowski";
email = "konrad.root.klawikowski@gmail.com";
github = "unrooted";
githubId = 30440603;
};
uralbash = {
email = "root@uralbash.ru";
github = "uralbash";
@ -12544,6 +12585,12 @@
githubId = 78392041;
name = "Winter";
};
wintrmvte = {
name = "Jakub Lutczyn";
email = "kubalutczyn@gmail.com";
github = "wintrmvte";
githubId = 41823252;
};
wirew0rm = {
email = "alex@wirew0rm.de";
github = "wirew0rm";
@ -12622,6 +12669,12 @@
githubId = 28888242;
name = "WORLDofPEACE";
};
wr0belj = {
name = "Jakub Wróbel";
email = "wrobel.jakub@protonmail.com";
github = "wr0belj";
githubId = 40501814;
};
wscott = {
email = "wsc9tt@gmail.com";
github = "wscott";
@ -12799,6 +12852,12 @@
githubId = 11229748;
name = "Lin Yinfeng";
};
ylecornec = {
email = "yves.stan.lecornec@tweag.io";
github = "ylecornec";
githubId = 5978566;
name = "Yves-Stan Le Cornec";
};
ylwghst = {
email = "ylwghst@onionmail.info";
github = "ylwghst";
@ -13367,4 +13426,10 @@
github = "vdot0x23";
githubId = 40716069;
};
jpagex = {
name = "Jérémy Pagé";
email = "contact@jeremypage.me";
github = "jpagex";
githubId = 635768;
};
}

View file

@ -4,20 +4,19 @@
# Example how to work with the `lib.maintainers` attrset.
# Can be used to check whether all user handles are still valid.
set -e
# nixpkgs='<nixpkgs>'
# if [ -n "$1" ]; then
set -o errexit -o noclobber -o nounset -o pipefail
shopt -s failglob inherit_errexit
function checkCommits {
local user="$1"
local tmp=$(mktemp)
local ret status tmp user
user="$1"
tmp=$(mktemp)
curl --silent -w "%{http_code}" \
"https://github.com/NixOS/nixpkgs/commits?author=$user" \
> "$tmp"
# the last line of tmp contains the http status
local status=$(tail -n1 "$tmp")
local ret=
status=$(tail -n1 "$tmp")
ret=
case $status in
200) if <"$tmp" grep -i "no commits found" > /dev/null; then
ret=1
@ -31,7 +30,7 @@ function checkCommits {
checkCommits "$user"
ret=$?
;;
*) printf "BAD STATUS: $(tail -n1 $tmp) for %s\n" "$user"; ret=1
*) printf "BAD STATUS: $(tail -n1 "$tmp") for %s\n" "$user"; ret=1
ret=1
;;
esac
@ -63,4 +62,5 @@ nix-instantiate -A lib.maintainers --eval --strict --json \
| jq -r '.[]|.github|select(.)' \
| parallel -j5 checkUser
# To check some arbitrary users:
# parallel -j100 checkUser ::: "eelco" "profpatsch" "Profpatsch" "a"

View file

@ -385,7 +385,7 @@ def check_results(
sys.exit(1)
def parse_plugin_line(line: str) -> PluginDesc:
branch = "master"
branch = "HEAD"
alias = None
name, repo = line.split("/")
if " as " in repo:

View file

@ -29,6 +29,20 @@ with lib.maintainers; {
scope = "Maintain ACME-related packages and modules.";
};
bazel = {
members = [
mboes
marsam
uri-canva
cbley
olebedev
groodt
aherrmann
ylecornec
];
scope = "Bazel build tool & related tools https://bazel.build/";
};
beam = {
members = [
ankhers
@ -233,6 +247,15 @@ with lib.maintainers; {
scope = "Maintain Podman and CRI-O related packages and modules.";
};
redcodelabs = {
members = [
unrooted
wr0belj
wintrmvte
];
scope = "Maintain Red Code Labs related packages and modules.";
};
sage = {
members = [
timokau
@ -259,4 +282,11 @@ with lib.maintainers; {
];
scope = "coqui-ai TTS (formerly Mozilla TTS) and leaf packages";
};
xfce = {
members = [
romildo
];
scope = "Maintain Xfce desktop environment and related packages.";
};
}

View file

@ -9,7 +9,7 @@ containers.database =
{ config =
{ config, pkgs, ... }:
{ services.postgresql.enable = true;
services.postgresql.package = pkgs.postgresql_9_6;
services.postgresql.package = pkgs.postgresql_10;
};
};
```

View file

@ -161,7 +161,7 @@ let
in rec {
inherit generatedSources;
inherit (optionsDoc) optionsJSON optionsXML optionsDocBook;
inherit (optionsDoc) optionsJSON optionsDocBook;
# Generate the NixOS manual.
manualHTML = runCommand "nixos-manual-html"

View file

@ -11,7 +11,7 @@ containers.database =
{ config =
{ config, pkgs, ... }:
{ services.postgresql.enable = true;
services.postgresql.package = pkgs.postgresql_9_6;
services.postgresql.package = pkgs.postgresql_10;
};
};
</programlisting>

View file

@ -273,6 +273,13 @@
<link xlink:href="options.html#opt-services.peertube.enable">services.peertube</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://maddy.email">maddy</link>, a
composable all-in-one mail server. Available as
<link xlink:href="options.html#opt-services.maddy.enable">services.maddy</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://sr.ht">sourcehut</link>, a

View file

@ -19,8 +19,31 @@
</section>
<section xml:id="sec-release-22.05-new-services">
<title>New Services</title>
<para>
</para>
<itemizedlist>
<listitem>
<para>
<link xlink:href="https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw">aesmd</link>,
the Intel SGX Architectural Enclave Service Manager. Available
as
<link linkend="opt-services.aesmd.enable">services.aesmd</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html">filebeat</link>,
a lightweight shipper for forwarding and centralizing log
data. Available as
<link linkend="opt-services.filebeat.enable">services.filebeat</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://github.com/ngoduykhanh/PowerDNS-Admin">PowerDNS-Admin</link>,
a web interface for the PowerDNS server. Available at
<link xlink:href="options.html#opt-services.powerdns-admin.enable">services.powerdns-admin</link>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-22.05-incompatibilities">
<title>Backward Incompatibilities</title>
@ -75,11 +98,85 @@
release based on GTK+3 and Python 3.
</para>
</listitem>
<listitem>
<para>
The <literal>writers.writePython2</literal> and corresponding
<literal>writers.writePython2Bin</literal> convenience
functions to create executable Python 2 scripts in the store
were removed in preparation of removal of the Python 2
interpreter. Scripts have to be converted to Python 3 for use
with <literal>writers.writePython3</literal> or
<literal>writers.writePyPy2</literal> needs to be used.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-22.05-notable-changes">
<title>Other Notable Changes</title>
<para>
</para>
<itemizedlist>
<listitem>
<para>
The option
<link linkend="opt-services.redis.servers">services.redis.servers</link>
was added to support per-application
<literal>redis-server</literal> which is more secure since
Redis databases are only mere key prefixes without any
configuration or ACL of their own. Backward-compatibility is
preserved by mapping old
<literal>services.redis.settings</literal> to
<literal>services.redis.servers.&quot;&quot;.settings</literal>,
but you are strongly encouraged to name each
<literal>redis-server</literal> instance after the application
using it, instead of keeping that nameless one. Except for the
nameless
<literal>services.redis.servers.&quot;&quot;</literal> still
accessible at <literal>127.0.0.1:6379</literal>, and to the
members of the Unix group <literal>redis</literal> through the
Unix socket <literal>/run/redis/redis.sock</literal>, all
other <literal>services.redis.servers.${serverName}</literal>
are only accessible by default to the members of the Unix
group <literal>redis-${serverName}</literal> through the Unix
socket <literal>/run/redis-${serverName}/redis.sock</literal>.
</para>
</listitem>
<listitem>
<para>
The
<literal>writers.writePyPy2</literal>/<literal>writers.writePyPy3</literal>
and corresponding
<literal>writers.writePyPy2Bin</literal>/<literal>writers.writePyPy3Bin</literal>
convenience functions to create executable Python 2/3 scripts
using the PyPy interpreter were added.
</para>
</listitem>
<listitem>
<para>
The <literal>influxdb2</literal> package was split into
<literal>influxdb2-server</literal> and
<literal>influxdb2-cli</literal>, matching the split that took
place upstream. A combined <literal>influxdb2</literal>
package is still provided in this release for backwards
compatibilty, but will be removed at a later date.
</para>
</listitem>
<listitem>
<para>
The <literal>services.unifi.openPorts</literal> option default
value of <literal>true</literal> is now deprecated and will be
changed to <literal>false</literal> in 22.11. Configurations
using this default will print a warning when rebuilt.
</para>
</listitem>
<listitem>
<para>
The option
<link linkend="opt-services.ssh.enableAskPassword">services.ssh.enableAskPassword</link>
was added, decoupling the setting of
<literal>SSH_ASKPASS</literal> from
<literal>services.xserver.enable</literal>. This allows easy
usage in non-X11 environments, e.g. Wayland.
</para>
</listitem>
</itemizedlist>
</section>
</section>

View file

@ -74,6 +74,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [PeerTube](https://joinpeertube.org/), developed by Framasoft, is the free and decentralized alternative to video platforms. Available at [services.peertube](options.html#opt-services.peertube.enable).
- [maddy](https://maddy.email), a composable all-in-one mail server. Available as [services.maddy](options.html#opt-services.maddy.enable).
- [sourcehut](https://sr.ht), a collection of tools useful for software development. Available as [services.sourcehut](options.html#opt-services.sourcehut.enable).
- [ucarp](https://download.pureftpd.org/pub/ucarp/README), an userspace implementation of the Common Address Redundancy Protocol (CARP). Available as [networking.ucarp](options.html#opt-networking.ucarp.enable).

View file

@ -8,6 +8,12 @@ In addition to numerous new and upgraded packages, this release has the followin
## New Services {#sec-release-22.05-new-services}
- [aesmd](https://github.com/intel/linux-sgx#install-the-intelr-sgx-psw), the Intel SGX Architectural Enclave Service Manager. Available as [services.aesmd](#opt-services.aesmd.enable).
- [filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html), a lightweight shipper for forwarding and centralizing log data. Available as [services.filebeat](#opt-services.filebeat.enable).
- [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin), a web interface for the PowerDNS server. Available at [services.powerdns-admin](options.html#opt-services.powerdns-admin.enable).
## Backward Incompatibilities {#sec-release-22.05-incompatibilities}
- `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`.
@ -32,4 +38,39 @@ In addition to numerous new and upgraded packages, this release has the followin
- `pkgs.claws-mail-gtk2`, representing Claws Mail's older release version three, was removed in order to get rid of Python 2.
Please switch to `claws-mail`, which is Claws Mail's latest release based on GTK+3 and Python 3.
- The `writers.writePython2` and corresponding `writers.writePython2Bin` convenience functions to create executable Python 2 scripts in the store were removed in preparation of removal of the Python 2 interpreter.
Scripts have to be converted to Python 3 for use with `writers.writePython3` or `writers.writePyPy2` needs to be used.
## Other Notable Changes {#sec-release-22.05-notable-changes}
- The option [services.redis.servers](#opt-services.redis.servers) was added
to support per-application `redis-server` which is more secure since Redis databases
are only mere key prefixes without any configuration or ACL of their own.
Backward-compatibility is preserved by mapping old `services.redis.settings`
to `services.redis.servers."".settings`, but you are strongly encouraged
to name each `redis-server` instance after the application using it,
instead of keeping that nameless one.
Except for the nameless `services.redis.servers.""`
still accessible at `127.0.0.1:6379`,
and to the members of the Unix group `redis`
through the Unix socket `/run/redis/redis.sock`,
all other `services.redis.servers.${serverName}`
are only accessible by default
to the members of the Unix group `redis-${serverName}`
through the Unix socket `/run/redis-${serverName}/redis.sock`.
- The `writers.writePyPy2`/`writers.writePyPy3` and corresponding `writers.writePyPy2Bin`/`writers.writePyPy3Bin` convenience functions to create executable Python 2/3 scripts using the PyPy interpreter were added.
- The `influxdb2` package was split into `influxdb2-server` and
`influxdb2-cli`, matching the split that took place upstream. A
combined `influxdb2` package is still provided in this release for
backwards compatibilty, but will be removed at a later date.
- The `services.unifi.openPorts` option default value of `true` is now deprecated and will be changed to `false` in 22.11.
Configurations using this default will print a warning when rebuilt.
- The option
[services.ssh.enableAskPassword](#opt-services.ssh.enableAskPassword) was
added, decoupling the setting of `SSH_ASKPASS` from
`services.xserver.enable`. This allows easy usage in non-X11 environments,
e.g. Wayland.

View file

@ -8,6 +8,7 @@
# as subcomponents (e.g. the container feature, or nixops if network
# expressions are ever made modular at the top level) can just use
# types.submodule instead of using eval-config.nix
evalConfigArgs@
{ # !!! system can be set modularly, would be nice to remove
system ? builtins.currentSystem
, # !!! is this argument needed any more? The pkgs argument can
@ -28,7 +29,7 @@
in if e == "" then [] else [(import e)]
}:
let extraArgs_ = extraArgs; pkgs_ = pkgs;
let pkgs_ = pkgs;
in
let
@ -51,28 +52,49 @@ let
};
};
noUserModules = lib.evalModules {
inherit prefix check;
modules = baseModules ++ extraModules ++ [ pkgsModule ];
args = extraArgs;
withWarnings = x:
lib.warnIf (evalConfigArgs?extraArgs) "The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
lib.warnIf (evalConfigArgs?check) "The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
x;
legacyModules =
lib.optional (evalConfigArgs?extraArgs) {
config = {
_module.args = extraArgs;
};
}
++ lib.optional (evalConfigArgs?check) {
config = {
_module.check = lib.mkDefault check;
};
};
allUserModules = modules ++ legacyModules;
noUserModules = lib.evalModules ({
inherit prefix;
modules = baseModules ++ extraModules ++ [ pkgsModule modulesModule ];
specialArgs =
{ modulesPath = builtins.toString ../modules; } // specialArgs;
});
# Extra arguments that are useful for constructing a similar configuration.
modulesModule = {
config = {
_module.args = {
inherit noUserModules baseModules extraModules modules;
};
};
};
# These are the extra arguments passed to every module. In
# particular, Nixpkgs is passed through the "pkgs" argument.
extraArgs = extraArgs_ // {
inherit noUserModules baseModules extraModules modules;
};
nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
in rec {
in withWarnings {
# Merge the option definitions in all modules, forming the full
# system configuration.
inherit (noUserModules.extendModules { inherit modules; })
config options _module type;
inherit (nixosWithUserModules) config options _module type;
inherit extraArgs;
inherit (_module.args) pkgs;
inherit (nixosWithUserModules._module.args) pkgs;
}

View file

@ -24,18 +24,25 @@
}:
let
# Replace functions by the string <function>
substFunction = x:
if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x
else if builtins.isList x then map substFunction x
# Make a value safe for JSON. Functions are replaced by the string "<function>",
# derivations are replaced with an attrset
# { _type = "derivation"; name = <name of that derivation>; }.
# We need to handle derivations specially because consumers want to know about them,
# but we can't easily use the type,name subset of keys (since type is often used as
# a module option and might cause confusion). Use _type,name instead to the same
# effect, since _type is already used by the module system.
substSpecial = x:
if lib.isDerivation x then { _type = "derivation"; name = x.name; }
else if builtins.isAttrs x then lib.mapAttrs (name: substSpecial) x
else if builtins.isList x then map substSpecial x
else if lib.isFunction x then "<function>"
else x;
optionsListDesc = lib.flip map optionsListVisible
optionsList = lib.flip map optionsListVisible
(opt: transformOptions opt
// lib.optionalAttrs (opt ? example) { example = substFunction opt.example; }
// lib.optionalAttrs (opt ? default) { default = substFunction opt.default; }
// lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }
// lib.optionalAttrs (opt ? example) { example = substSpecial opt.example; }
// lib.optionalAttrs (opt ? default) { default = substSpecial opt.default; }
// lib.optionalAttrs (opt ? type) { type = substSpecial opt.type; }
// lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) { relatedPackages = genRelatedPackages opt.relatedPackages opt.name; }
);
@ -69,96 +76,25 @@ let
+ "</listitem>";
in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
# Custom "less" that pushes up all the things ending in ".enable*"
# and ".package*"
optionLess = a: b:
let
ise = lib.hasPrefix "enable";
isp = lib.hasPrefix "package";
cmp = lib.splitByAndCompare ise lib.compare
(lib.splitByAndCompare isp lib.compare lib.compare);
in lib.compareLists cmp a.loc b.loc < 0;
# Remove invisible and internal options.
optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options);
# Customly sort option list for the man page.
# Always ensure that the sort order matches sortXML.py!
optionsList = lib.sort optionLess optionsListDesc;
# Convert the list of options into an XML file.
# This file is *not* sorted sorted to save on eval time, since the docbook XML
# and the manpage depend on it and thus we evaluate this on every system rebuild.
optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsListDesc);
optionsNix = builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList);
# TODO: declarations: link to github
singleAsciiDoc = name: value: ''
== ${name}
${value.description}
[discrete]
=== details
Type:: ${value.type}
${ if lib.hasAttr "default" value
then ''
Default::
+
----
${builtins.toJSON value.default}
----
''
else "No Default:: {blank}"
}
${ if value.readOnly
then "Read Only:: {blank}"
else ""
}
${ if lib.hasAttr "example" value
then ''
Example::
+
----
${builtins.toJSON value.example}
----
''
else "No Example:: {blank}"
}
'';
singleMDDoc = name: value: ''
## ${lib.escape [ "<" ">" ] name}
${value.description}
${lib.optionalString (value ? type) ''
*_Type_*:
${value.type}
''}
${lib.optionalString (value ? default) ''
*_Default_*
```
${builtins.toJSON value.default}
```
''}
${lib.optionalString (value ? example) ''
*_Example_*
```
${builtins.toJSON value.example}
```
''}
'';
in {
in rec {
inherit optionsNix;
optionsAsciiDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleAsciiDoc optionsNix);
optionsAsciiDoc = pkgs.runCommand "options.adoc" {} ''
${pkgs.python3Minimal}/bin/python ${./generateAsciiDoc.py} \
< ${optionsJSON}/share/doc/nixos/options.json \
> $out
'';
optionsMDDoc = lib.concatStringsSep "\n" (lib.mapAttrsToList singleMDDoc optionsNix);
optionsCommonMark = pkgs.runCommand "options.md" {} ''
${pkgs.python3Minimal}/bin/python ${./generateCommonMark.py} \
< ${optionsJSON}/share/doc/nixos/options.json \
> $out
'';
optionsJSON = pkgs.runCommand "options.json"
{ meta.description = "List of NixOS options in JSON format";
@ -176,7 +112,19 @@ in {
mkdir -p $out/nix-support
echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
''; # */
'';
# Convert options.json into an XML file.
# The actual generation of the xml file is done in nix purely for the convenience
# of not having to generate the xml some other way
optionsXML = pkgs.runCommand "options.xml" {} ''
export NIX_STORE_DIR=$TMPDIR/store
export NIX_STATE_DIR=$TMPDIR/state
${pkgs.nix}/bin/nix-instantiate \
--eval --xml --strict ${./optionsJSONtoXML.nix} \
--argstr file ${optionsJSON}/share/doc/nixos/options.json \
> "$out"
'';
optionsDocBook = pkgs.runCommand "options-docbook.xml" {} ''
optionsXML=${optionsXML}

View file

@ -0,0 +1,37 @@
import json
import sys
options = json.load(sys.stdin)
# TODO: declarations: link to github
for (name, value) in options.items():
print(f'== {name}')
print()
print(value['description'])
print()
print('[discrete]')
print('=== details')
print()
print(f'Type:: {value["type"]}')
if 'default' in value:
print('Default::')
print('+')
print('----')
print(json.dumps(value['default'], ensure_ascii=False, separators=(',', ':')))
print('----')
print()
else:
print('No Default:: {blank}')
if value['readOnly']:
print('Read Only:: {blank}')
else:
print()
if 'example' in value:
print('Example::')
print('+')
print('----')
print(json.dumps(value['example'], ensure_ascii=False, separators=(',', ':')))
print('----')
print()
else:
print('No Example:: {blank}')
print()

View file

@ -0,0 +1,27 @@
import json
import sys
options = json.load(sys.stdin)
for (name, value) in options.items():
print('##', name.replace('<', '\\<').replace('>', '\\>'))
print(value['description'])
print()
if 'type' in value:
print('*_Type_*:')
print(value['type'])
print()
print()
if 'default' in value:
print('*_Default_*')
print('```')
print(json.dumps(value['default'], ensure_ascii=False, separators=(',', ':')))
print('```')
print()
print()
if 'example' in value:
print('*_Example_*')
print('```')
print(json.dumps(value['example'], ensure_ascii=False, separators=(',', ':')))
print('```')
print()
print()

View file

@ -189,7 +189,7 @@
</xsl:template>
<xsl:template match="derivation">
<xsl:template match="attrs[attr[@name = '_type' and string[@value = 'derivation']]]">
<replaceable>(build of <xsl:value-of select="attr[@name = 'name']/string/@value" />)</replaceable>
</xsl:template>

View file

@ -0,0 +1,6 @@
{ file }:
builtins.attrValues
(builtins.mapAttrs
(name: def: def // { inherit name; })
(builtins.fromJSON (builtins.readFile file)))

View file

@ -19,7 +19,6 @@ def sortKey(opt):
for p in opt.findall('attr[@name="loc"]/list/string')
]
# always ensure that the sort order matches the order used in the nix expression!
options.sort(key=sortKey)
doc = ET.Element("expr")

View file

@ -21,8 +21,15 @@ stdenv.mkDerivation {
# for nix-store --load-db.
cp $closureInfo/registration nix-path-registration
# 64 cores on i686 does not work
# fails with FATAL ERROR: mangle2:: xz compress failed with error code 5
if ((NIX_BUILD_CORES > 48)); then
NIX_BUILD_CORES=48
fi
# Generate the squashfs image.
mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $out \
-no-hardlinks -keep-as-directory -all-root -b 1048576 -comp ${comp}
-no-hardlinks -keep-as-directory -all-root -b 1048576 -comp ${comp} \
-processors $NIX_BUILD_CORES
'';
}

View file

@ -1,7 +1,7 @@
{ config, lib }:
{ lib, systemdUtils }:
with systemdUtils.lib;
with lib;
with import ./systemd-lib.nix { inherit config lib pkgs; };
let
checkService = checkUnitConfig "Service" [

View file

@ -0,0 +1,32 @@
{ lib
, python3Packages
, enableOCR ? false
, qemu_pkg ? qemu_test
, coreutils
, imagemagick_light
, libtiff
, netpbm
, qemu_test
, socat
, tesseract4
, vde2
}:
python3Packages.buildPythonApplication rec {
pname = "nixos-test-driver";
version = "1.0";
src = ./.;
propagatedBuildInputs = [ coreutils netpbm python3Packages.colorama python3Packages.ptpython qemu_pkg socat vde2 ]
++ (lib.optionals enableOCR [ imagemagick_light tesseract4 ]);
doCheck = true;
checkInputs = with python3Packages; [ mypy pylint black ];
checkPhase = ''
mypy --disallow-untyped-defs \
--no-implicit-optional \
--ignore-missing-imports ${src}/test_driver
pylint --errors-only ${src}/test_driver
black --check --diff ${src}/test_driver
'';
}

View file

@ -0,0 +1,13 @@
from setuptools import setup, find_packages
setup(
name="nixos-test-driver",
version='1.0',
packages=find_packages(),
entry_points={
"console_scripts": [
"nixos-test-driver=test_driver:main",
"generate-driver-symbols=test_driver:generate_driver_symbols"
]
},
)

View file

@ -0,0 +1,100 @@
from pathlib import Path
import argparse
import ptpython.repl
import os
import time
from test_driver.logger import rootlog
from test_driver.driver import Driver
class EnvDefault(argparse.Action):
"""An argpars Action that takes values from the specified
environment variable as the flags default value.
"""
def __init__(self, envvar, required=False, default=None, nargs=None, **kwargs): # type: ignore
if not default and envvar:
if envvar in os.environ:
if nargs is not None and (nargs.isdigit() or nargs in ["*", "+"]):
default = os.environ[envvar].split()
else:
default = os.environ[envvar]
kwargs["help"] = (
kwargs["help"] + f" (default from environment: {default})"
)
if required and default:
required = False
super(EnvDefault, self).__init__(
default=default, required=required, nargs=nargs, **kwargs
)
def __call__(self, parser, namespace, values, option_string=None): # type: ignore
setattr(namespace, self.dest, values)
def main() -> None:
arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
arg_parser.add_argument(
"-K",
"--keep-vm-state",
help="re-use a VM state coming from a previous run",
action="store_true",
)
arg_parser.add_argument(
"-I",
"--interactive",
help="drop into a python repl and run the tests interactively",
action="store_true",
)
arg_parser.add_argument(
"--start-scripts",
metavar="START-SCRIPT",
action=EnvDefault,
envvar="startScripts",
nargs="*",
help="start scripts for participating virtual machines",
)
arg_parser.add_argument(
"--vlans",
metavar="VLAN",
action=EnvDefault,
envvar="vlans",
nargs="*",
help="vlans to span by the driver",
)
arg_parser.add_argument(
"testscript",
action=EnvDefault,
envvar="testScript",
help="the test script to run",
type=Path,
)
args = arg_parser.parse_args()
if not args.keep_vm_state:
rootlog.info("Machine state will be reset. To keep it, pass --keep-vm-state")
with Driver(
args.start_scripts, args.vlans, args.testscript.read_text(), args.keep_vm_state
) as driver:
if args.interactive:
ptpython.repl.embed(driver.test_symbols(), {})
else:
tic = time.time()
driver.run_tests()
toc = time.time()
rootlog.info(f"test script finished in {(toc-tic):.2f}s")
def generate_driver_symbols() -> None:
"""
This generates a file with symbols of the test-driver code that can be used
in user's test scripts. That list is then used by pyflakes to lint those
scripts.
"""
d = Driver([], [], "")
test_symbols = d.test_symbols()
with open("driver-symbols", "w") as fp:
fp.write(",".join(test_symbols.keys()))

View file

@ -0,0 +1,161 @@
from contextlib import contextmanager
from pathlib import Path
from typing import Any, Dict, Iterator, List
import os
import tempfile
from test_driver.logger import rootlog
from test_driver.machine import Machine, NixStartScript, retry
from test_driver.vlan import VLan
class Driver:
"""A handle to the driver that sets up the environment
and runs the tests"""
tests: str
vlans: List[VLan]
machines: List[Machine]
def __init__(
self,
start_scripts: List[str],
vlans: List[int],
tests: str,
keep_vm_state: bool = False,
):
self.tests = tests
tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
tmp_dir.mkdir(mode=0o700, exist_ok=True)
with rootlog.nested("start all VLans"):
self.vlans = [VLan(nr, tmp_dir) for nr in vlans]
def cmd(scripts: List[str]) -> Iterator[NixStartScript]:
for s in scripts:
yield NixStartScript(s)
self.machines = [
Machine(
start_command=cmd,
keep_vm_state=keep_vm_state,
name=cmd.machine_name,
tmp_dir=tmp_dir,
)
for cmd in cmd(start_scripts)
]
def __enter__(self) -> "Driver":
return self
def __exit__(self, *_: Any) -> None:
with rootlog.nested("cleanup"):
for machine in self.machines:
machine.release()
def subtest(self, name: str) -> Iterator[None]:
"""Group logs under a given test name"""
with rootlog.nested(name):
try:
yield
return True
except Exception as e:
rootlog.error(f'Test "{name}" failed with error: "{e}"')
raise e
def test_symbols(self) -> Dict[str, Any]:
@contextmanager
def subtest(name: str) -> Iterator[None]:
return self.subtest(name)
general_symbols = dict(
start_all=self.start_all,
test_script=self.test_script,
machines=self.machines,
vlans=self.vlans,
driver=self,
log=rootlog,
os=os,
create_machine=self.create_machine,
subtest=subtest,
run_tests=self.run_tests,
join_all=self.join_all,
retry=retry,
serial_stdout_off=self.serial_stdout_off,
serial_stdout_on=self.serial_stdout_on,
Machine=Machine, # for typing
)
machine_symbols = {m.name: m for m in self.machines}
# If there's exactly one machine, make it available under the name
# "machine", even if it's not called that.
if len(self.machines) == 1:
(machine_symbols["machine"],) = self.machines
vlan_symbols = {
f"vlan{v.nr}": self.vlans[idx] for idx, v in enumerate(self.vlans)
}
print(
"additionally exposed symbols:\n "
+ ", ".join(map(lambda m: m.name, self.machines))
+ ",\n "
+ ", ".join(map(lambda v: f"vlan{v.nr}", self.vlans))
+ ",\n "
+ ", ".join(list(general_symbols.keys()))
)
return {**general_symbols, **machine_symbols, **vlan_symbols}
def test_script(self) -> None:
"""Run the test script"""
with rootlog.nested("run the VM test script"):
symbols = self.test_symbols() # call eagerly
exec(self.tests, symbols, None)
def run_tests(self) -> None:
"""Run the test script (for non-interactive test runs)"""
self.test_script()
# TODO: Collect coverage data
for machine in self.machines:
if machine.is_up():
machine.execute("sync")
def start_all(self) -> None:
"""Start all machines"""
with rootlog.nested("start all VMs"):
for machine in self.machines:
machine.start()
def join_all(self) -> None:
"""Wait for all machines to shut down"""
with rootlog.nested("wait for all VMs to finish"):
for machine in self.machines:
machine.wait_for_shutdown()
def create_machine(self, args: Dict[str, Any]) -> Machine:
rootlog.warning(
"Using legacy create_machine(), please instantiate the"
"Machine class directly, instead"
)
tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
tmp_dir.mkdir(mode=0o700, exist_ok=True)
if args.get("startCommand"):
start_command: str = args.get("startCommand", "")
cmd = NixStartScript(start_command)
name = args.get("name", cmd.machine_name)
else:
cmd = Machine.create_startcommand(args) # type: ignore
name = args.get("name", "machine")
return Machine(
tmp_dir=tmp_dir,
start_command=cmd,
name=name,
keep_vm_state=args.get("keep_vm_state", False),
allow_reboot=args.get("allow_reboot", False),
)
def serial_stdout_on(self) -> None:
rootlog._print_serial_logs = True
def serial_stdout_off(self) -> None:
rootlog._print_serial_logs = False

View file

@ -0,0 +1,101 @@
from colorama import Style
from contextlib import contextmanager
from typing import Any, Dict, Iterator
from queue import Queue, Empty
from xml.sax.saxutils import XMLGenerator
import codecs
import os
import sys
import time
import unicodedata
class Logger:
def __init__(self) -> None:
self.logfile = os.environ.get("LOGFILE", "/dev/null")
self.logfile_handle = codecs.open(self.logfile, "wb")
self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
self.queue: "Queue[Dict[str, str]]" = Queue()
self.xml.startDocument()
self.xml.startElement("logfile", attrs={})
self._print_serial_logs = True
@staticmethod
def _eprint(*args: object, **kwargs: Any) -> None:
print(*args, file=sys.stderr, **kwargs)
def close(self) -> None:
self.xml.endElement("logfile")
self.xml.endDocument()
self.logfile_handle.close()
def sanitise(self, message: str) -> str:
return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
if "machine" in attributes:
return "{}: {}".format(attributes["machine"], message)
return message
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
self.xml.startElement("line", attributes)
self.xml.characters(message)
self.xml.endElement("line")
def info(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
def warning(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
def error(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
sys.exit(1)
def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
self._eprint(self.maybe_prefix(message, attributes))
self.drain_log_queue()
self.log_line(message, attributes)
def log_serial(self, message: str, machine: str) -> None:
self.enqueue({"msg": message, "machine": machine, "type": "serial"})
if self._print_serial_logs:
self._eprint(
Style.DIM + "{} # {}".format(machine, message) + Style.RESET_ALL
)
def enqueue(self, item: Dict[str, str]) -> None:
self.queue.put(item)
def drain_log_queue(self) -> None:
try:
while True:
item = self.queue.get_nowait()
msg = self.sanitise(item["msg"])
del item["msg"]
self.log_line(msg, item)
except Empty:
pass
@contextmanager
def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
self._eprint(self.maybe_prefix(message, attributes))
self.xml.startElement("nest", attrs={})
self.xml.startElement("head", attributes)
self.xml.characters(message)
self.xml.endElement("head")
tic = time.time()
self.drain_log_queue()
yield
self.drain_log_queue()
toc = time.time()
self.log("(finished: {}, in {:.2f} seconds)".format(message, toc - tic))
self.xml.endElement("nest")
rootlog = Logger()

View file

@ -1,19 +1,11 @@
#! /somewhere/python3
from contextlib import contextmanager, _GeneratorContextManager
from queue import Queue, Empty
from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List, Iterable
from xml.sax.saxutils import XMLGenerator
from colorama import Style
from contextlib import _GeneratorContextManager
from pathlib import Path
import queue
import io
import threading
import argparse
from queue import Queue
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
import base64
import codecs
import io
import os
import ptpython.repl
import pty
import queue
import re
import shlex
import shutil
@ -21,8 +13,10 @@ import socket
import subprocess
import sys
import tempfile
import threading
import time
import unicodedata
from test_driver.logger import rootlog
CHAR_TO_KEY = {
"A": "shift-a",
@ -88,115 +82,10 @@ CHAR_TO_KEY = {
}
class Logger:
def __init__(self) -> None:
self.logfile = os.environ.get("LOGFILE", "/dev/null")
self.logfile_handle = codecs.open(self.logfile, "wb")
self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
self.queue: "Queue[Dict[str, str]]" = Queue()
self.xml.startDocument()
self.xml.startElement("logfile", attrs={})
self._print_serial_logs = True
@staticmethod
def _eprint(*args: object, **kwargs: Any) -> None:
print(*args, file=sys.stderr, **kwargs)
def close(self) -> None:
self.xml.endElement("logfile")
self.xml.endDocument()
self.logfile_handle.close()
def sanitise(self, message: str) -> str:
return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
if "machine" in attributes:
return "{}: {}".format(attributes["machine"], message)
return message
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
self.xml.startElement("line", attributes)
self.xml.characters(message)
self.xml.endElement("line")
def info(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
def warning(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
def error(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
sys.exit(1)
def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
self._eprint(self.maybe_prefix(message, attributes))
self.drain_log_queue()
self.log_line(message, attributes)
def log_serial(self, message: str, machine: str) -> None:
self.enqueue({"msg": message, "machine": machine, "type": "serial"})
if self._print_serial_logs:
self._eprint(
Style.DIM + "{} # {}".format(machine, message) + Style.RESET_ALL
)
def enqueue(self, item: Dict[str, str]) -> None:
self.queue.put(item)
def drain_log_queue(self) -> None:
try:
while True:
item = self.queue.get_nowait()
msg = self.sanitise(item["msg"])
del item["msg"]
self.log_line(msg, item)
except Empty:
pass
@contextmanager
def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
self._eprint(self.maybe_prefix(message, attributes))
self.xml.startElement("nest", attrs={})
self.xml.startElement("head", attributes)
self.xml.characters(message)
self.xml.endElement("head")
tic = time.time()
self.drain_log_queue()
yield
self.drain_log_queue()
toc = time.time()
self.log("(finished: {}, in {:.2f} seconds)".format(message, toc - tic))
self.xml.endElement("nest")
rootlog = Logger()
def make_command(args: list) -> str:
return " ".join(map(shlex.quote, (map(str, args))))
def retry(fn: Callable, timeout: int = 900) -> None:
"""Call the given function repeatedly, with 1 second intervals,
until it returns True or a timeout is reached.
"""
for _ in range(timeout):
if fn(False):
return
time.sleep(1)
if not fn(True):
raise Exception(f"action timed out after {timeout} seconds")
def _perform_ocr_on_screenshot(
screenshot_path: str, model_ids: Iterable[int]
) -> List[str]:
@ -228,6 +117,20 @@ def _perform_ocr_on_screenshot(
return model_results
def retry(fn: Callable, timeout: int = 900) -> None:
"""Call the given function repeatedly, with 1 second intervals,
until it returns True or a timeout is reached.
"""
for _ in range(timeout):
if fn(False):
return
time.sleep(1)
if not fn(True):
raise Exception(f"action timed out after {timeout} seconds")
class StartCommand:
"""The Base Start Command knows how to append the necesary
runtime qemu options as determined by a particular test driver
@ -1066,286 +969,3 @@ class Machine:
self.shell.close()
self.monitor.close()
self.serial_thread.join()
class VLan:
"""This class handles a VLAN that the run-vm scripts identify via its
number handles. The network's lifetime equals the object's lifetime.
"""
nr: int
socket_dir: Path
process: subprocess.Popen
pid: int
fd: io.TextIOBase
def __repr__(self) -> str:
return f"<Vlan Nr. {self.nr}>"
def __init__(self, nr: int, tmp_dir: Path):
self.nr = nr
self.socket_dir = tmp_dir / f"vde{self.nr}.ctl"
# TODO: don't side-effect environment here
os.environ[f"QEMU_VDE_SOCKET_{self.nr}"] = str(self.socket_dir)
rootlog.info("start vlan")
pty_master, pty_slave = pty.openpty()
self.process = subprocess.Popen(
["vde_switch", "-s", self.socket_dir, "--dirmode", "0700"],
stdin=pty_slave,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=False,
)
self.pid = self.process.pid
self.fd = os.fdopen(pty_master, "w")
self.fd.write("version\n")
# TODO: perl version checks if this can be read from
# an if not, dies. we could hang here forever. Fix it.
assert self.process.stdout is not None
self.process.stdout.readline()
if not (self.socket_dir / "ctl").exists():
rootlog.error("cannot start vde_switch")
rootlog.info(f"running vlan (pid {self.pid})")
def __del__(self) -> None:
rootlog.info(f"kill vlan (pid {self.pid})")
self.fd.close()
self.process.terminate()
class Driver:
"""A handle to the driver that sets up the environment
and runs the tests"""
tests: str
vlans: List[VLan]
machines: List[Machine]
def __init__(
self,
start_scripts: List[str],
vlans: List[int],
tests: str,
keep_vm_state: bool = False,
):
self.tests = tests
tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
tmp_dir.mkdir(mode=0o700, exist_ok=True)
with rootlog.nested("start all VLans"):
self.vlans = [VLan(nr, tmp_dir) for nr in vlans]
def cmd(scripts: List[str]) -> Iterator[NixStartScript]:
for s in scripts:
yield NixStartScript(s)
self.machines = [
Machine(
start_command=cmd,
keep_vm_state=keep_vm_state,
name=cmd.machine_name,
tmp_dir=tmp_dir,
)
for cmd in cmd(start_scripts)
]
def __enter__(self) -> "Driver":
return self
def __exit__(self, *_: Any) -> None:
with rootlog.nested("cleanup"):
for machine in self.machines:
machine.release()
def subtest(self, name: str) -> Iterator[None]:
"""Group logs under a given test name"""
with rootlog.nested(name):
try:
yield
return True
except Exception as e:
rootlog.error(f'Test "{name}" failed with error: "{e}"')
raise e
def test_symbols(self) -> Dict[str, Any]:
@contextmanager
def subtest(name: str) -> Iterator[None]:
return self.subtest(name)
general_symbols = dict(
start_all=self.start_all,
test_script=self.test_script,
machines=self.machines,
vlans=self.vlans,
driver=self,
log=rootlog,
os=os,
create_machine=self.create_machine,
subtest=subtest,
run_tests=self.run_tests,
join_all=self.join_all,
retry=retry,
serial_stdout_off=self.serial_stdout_off,
serial_stdout_on=self.serial_stdout_on,
Machine=Machine, # for typing
)
machine_symbols = {m.name: m for m in self.machines}
# If there's exactly one machine, make it available under the name
# "machine", even if it's not called that.
if len(self.machines) == 1:
(machine_symbols["machine"],) = self.machines
vlan_symbols = {
f"vlan{v.nr}": self.vlans[idx] for idx, v in enumerate(self.vlans)
}
print(
"additionally exposed symbols:\n "
+ ", ".join(map(lambda m: m.name, self.machines))
+ ",\n "
+ ", ".join(map(lambda v: f"vlan{v.nr}", self.vlans))
+ ",\n "
+ ", ".join(list(general_symbols.keys()))
)
return {**general_symbols, **machine_symbols, **vlan_symbols}
def test_script(self) -> None:
"""Run the test script"""
with rootlog.nested("run the VM test script"):
symbols = self.test_symbols() # call eagerly
exec(self.tests, symbols, None)
def run_tests(self) -> None:
"""Run the test script (for non-interactive test runs)"""
self.test_script()
# TODO: Collect coverage data
for machine in self.machines:
if machine.is_up():
machine.execute("sync")
def start_all(self) -> None:
"""Start all machines"""
with rootlog.nested("start all VMs"):
for machine in self.machines:
machine.start()
def join_all(self) -> None:
"""Wait for all machines to shut down"""
with rootlog.nested("wait for all VMs to finish"):
for machine in self.machines:
machine.wait_for_shutdown()
def create_machine(self, args: Dict[str, Any]) -> Machine:
rootlog.warning(
"Using legacy create_machine(), please instantiate the"
"Machine class directly, instead"
)
tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
tmp_dir.mkdir(mode=0o700, exist_ok=True)
if args.get("startCommand"):
start_command: str = args.get("startCommand", "")
cmd = NixStartScript(start_command)
name = args.get("name", cmd.machine_name)
else:
cmd = Machine.create_startcommand(args) # type: ignore
name = args.get("name", "machine")
return Machine(
tmp_dir=tmp_dir,
start_command=cmd,
name=name,
keep_vm_state=args.get("keep_vm_state", False),
allow_reboot=args.get("allow_reboot", False),
)
def serial_stdout_on(self) -> None:
rootlog._print_serial_logs = True
def serial_stdout_off(self) -> None:
rootlog._print_serial_logs = False
class EnvDefault(argparse.Action):
"""An argpars Action that takes values from the specified
environment variable as the flags default value.
"""
def __init__(self, envvar, required=False, default=None, nargs=None, **kwargs): # type: ignore
if not default and envvar:
if envvar in os.environ:
if nargs is not None and (nargs.isdigit() or nargs in ["*", "+"]):
default = os.environ[envvar].split()
else:
default = os.environ[envvar]
kwargs["help"] = (
kwargs["help"] + f" (default from environment: {default})"
)
if required and default:
required = False
super(EnvDefault, self).__init__(
default=default, required=required, nargs=nargs, **kwargs
)
def __call__(self, parser, namespace, values, option_string=None): # type: ignore
setattr(namespace, self.dest, values)
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser(prog="nixos-test-driver")
arg_parser.add_argument(
"-K",
"--keep-vm-state",
help="re-use a VM state coming from a previous run",
action="store_true",
)
arg_parser.add_argument(
"-I",
"--interactive",
help="drop into a python repl and run the tests interactively",
action="store_true",
)
arg_parser.add_argument(
"--start-scripts",
metavar="START-SCRIPT",
action=EnvDefault,
envvar="startScripts",
nargs="*",
help="start scripts for participating virtual machines",
)
arg_parser.add_argument(
"--vlans",
metavar="VLAN",
action=EnvDefault,
envvar="vlans",
nargs="*",
help="vlans to span by the driver",
)
arg_parser.add_argument(
"testscript",
action=EnvDefault,
envvar="testScript",
help="the test script to run",
type=Path,
)
args = arg_parser.parse_args()
if not args.keep_vm_state:
rootlog.info("Machine state will be reset. To keep it, pass --keep-vm-state")
with Driver(
args.start_scripts, args.vlans, args.testscript.read_text(), args.keep_vm_state
) as driver:
if args.interactive:
ptpython.repl.embed(driver.test_symbols(), {})
else:
tic = time.time()
driver.run_tests()
toc = time.time()
rootlog.info(f"test script finished in {(toc-tic):.2f}s")

View file

@ -0,0 +1,58 @@
from pathlib import Path
import io
import os
import pty
import subprocess
from test_driver.logger import rootlog
class VLan:
"""This class handles a VLAN that the run-vm scripts identify via its
number handles. The network's lifetime equals the object's lifetime.
"""
nr: int
socket_dir: Path
process: subprocess.Popen
pid: int
fd: io.TextIOBase
def __repr__(self) -> str:
return f"<Vlan Nr. {self.nr}>"
def __init__(self, nr: int, tmp_dir: Path):
self.nr = nr
self.socket_dir = tmp_dir / f"vde{self.nr}.ctl"
# TODO: don't side-effect environment here
os.environ[f"QEMU_VDE_SOCKET_{self.nr}"] = str(self.socket_dir)
rootlog.info("start vlan")
pty_master, pty_slave = pty.openpty()
self.process = subprocess.Popen(
["vde_switch", "-s", self.socket_dir, "--dirmode", "0700"],
stdin=pty_slave,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=False,
)
self.pid = self.process.pid
self.fd = os.fdopen(pty_master, "w")
self.fd.write("version\n")
# TODO: perl version checks if this can be read from
# an if not, dies. we could hang here forever. Fix it.
assert self.process.stdout is not None
self.process.stdout.readline()
if not (self.socket_dir / "ctl").exists():
rootlog.error("cannot start vde_switch")
rootlog.info(f"running vlan (pid {self.pid})")
def __del__(self) -> None:
rootlog.info(f"kill vlan (pid {self.pid})")
self.fd.close()
self.process.terminate()

View file

@ -16,65 +16,6 @@ rec {
inherit pkgs;
# Reifies and correctly wraps the python test driver for
# the respective qemu version and with or without ocr support
pythonTestDriver = {
qemu_pkg ? pkgs.qemu_test
, enableOCR ? false
}:
let
name = "nixos-test-driver";
testDriverScript = ./test-driver/test-driver.py;
ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
in stdenv.mkDerivation {
inherit name;
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ (python3.withPackages (p: [ p.ptpython p.colorama ])) ];
checkInputs = with python3Packages; [ pylint black mypy ];
dontUnpack = true;
preferLocalBuild = true;
buildPhase = ''
python <<EOF
from pydoc import importfile
with open('driver-symbols', 'w') as fp:
t = importfile('${testDriverScript}')
d = t.Driver([],[],"")
test_symbols = d.test_symbols()
fp.write(','.join(test_symbols.keys()))
EOF
'';
doCheck = true;
checkPhase = ''
mypy --disallow-untyped-defs \
--no-implicit-optional \
--ignore-missing-imports ${testDriverScript}
pylint --errors-only ${testDriverScript}
black --check --diff ${testDriverScript}
'';
installPhase =
''
mkdir -p $out/bin
cp ${testDriverScript} $out/bin/nixos-test-driver
chmod u+x $out/bin/nixos-test-driver
# TODO: copy user script part into this file (append)
wrapProgram $out/bin/nixos-test-driver \
--argv0 ${name} \
--prefix PATH : "${lib.makeBinPath [ qemu_pkg vde2 netpbm coreutils socat ]}" \
${lib.optionalString enableOCR
"--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
install -m 0644 -vD driver-symbols $out/nix-support/driver-symbols
'';
};
# Run an automated test suite in the given virtual network.
runTests = { driver, pos }:
stdenv.mkDerivation {
@ -112,8 +53,15 @@ rec {
, passthru ? {}
}:
let
# FIXME: get this pkg from the module system
testDriver = pythonTestDriver { inherit qemu_pkg enableOCR;};
# Reifies and correctly wraps the python test driver for
# the respective qemu version and with or without ocr support
testDriver = pkgs.callPackage ./test-driver {
inherit enableOCR;
qemu_pkg = qemu_test;
imagemagick_light = imagemagick_light.override { inherit libtiff; };
tesseract4 = tesseract4.override { enableLanguages = [ "eng" ]; };
};
testDriverName =
let
@ -178,10 +126,11 @@ rec {
echo -n "$testScript" > $out/test-script
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
${testDriver}/bin/generate-driver-symbols
${lib.optionalString (!skipLint) ''
PYFLAKES_BUILTINS="$(
echo -n ${lib.escapeShellArg (lib.concatStringsSep "," nodeHostNames)},
< ${lib.escapeShellArg "${testDriver}/nix-support/driver-symbols"}
< ${lib.escapeShellArg "driver-symbols"}
)" ${python3Packages.pyflakes}/bin/pyflakes $out/test-script
''}

View file

@ -1,4 +1,4 @@
pkgs: with pkgs.lib;
{ lib, config, pkgs }: with lib;
rec {
@ -165,4 +165,9 @@ rec {
${builtins.toJSON set}
EOF
'';
systemdUtils = {
lib = import ./systemd-lib.nix { inherit lib config pkgs; };
unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; };
};
}

View file

@ -1,12 +1,13 @@
# /etc files related to networking, such as /etc/services.
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.networking;
opt = options.networking;
localhostMultiple = any (elem "localhost") (attrValues (removeAttrs cfg.hosts [ "127.0.0.1" "::1" ]));
@ -78,6 +79,7 @@ in
httpProxy = lib.mkOption {
type = types.nullOr types.str;
default = cfg.proxy.default;
defaultText = literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the http_proxy environment variable.
'';
@ -87,6 +89,7 @@ in
httpsProxy = lib.mkOption {
type = types.nullOr types.str;
default = cfg.proxy.default;
defaultText = literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the https_proxy environment variable.
'';
@ -96,6 +99,7 @@ in
ftpProxy = lib.mkOption {
type = types.nullOr types.str;
default = cfg.proxy.default;
defaultText = literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the ftp_proxy environment variable.
'';
@ -105,6 +109,7 @@ in
rsyncProxy = lib.mkOption {
type = types.nullOr types.str;
default = cfg.proxy.default;
defaultText = literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the rsync_proxy environment variable.
'';
@ -114,6 +119,7 @@ in
allProxy = lib.mkOption {
type = types.nullOr types.str;
default = cfg.proxy.default;
defaultText = literalExpression "config.${opt.proxy.default}";
description = ''
This option specifies the all_proxy environment variable.
'';

View file

@ -41,12 +41,17 @@ let
pkgs.zstd
];
defaultPackages = map (pkg: setPrio ((pkg.meta.priority or 5) + 3) pkg)
[ pkgs.nano
pkgs.perl
pkgs.rsync
pkgs.strace
];
defaultPackageNames =
[ "nano"
"perl"
"rsync"
"strace"
];
defaultPackages =
map
(n: let pkg = pkgs.${n}; in setPrio ((pkg.meta.priority or 5) + 3) pkg)
defaultPackageNames;
defaultPackagesText = "[ ${concatMapStringsSep " " (n: "pkgs.${n}") defaultPackageNames } ]";
in
@ -73,6 +78,11 @@ in
defaultPackages = mkOption {
type = types.listOf types.package;
default = defaultPackages;
defaultText = literalDocBook ''
these packages, with their <literal>meta.priority</literal> numerically increased
(thus lowering their installation priority):
<programlisting>${defaultPackagesText}</programlisting>
'';
example = [];
description = ''
Set of default packages that aren't strictly necessary

View file

@ -0,0 +1,47 @@
{ config, lib, ... }:
with lib;
let
cfg = config.hardware.cpu.intel.sgx.provision;
defaultGroup = "sgx_prv";
in
{
options.hardware.cpu.intel.sgx.provision = {
enable = mkEnableOption "access to the Intel SGX provisioning device";
user = mkOption {
description = "Owner to assign to the SGX provisioning device.";
type = types.str;
default = "root";
};
group = mkOption {
description = "Group to assign to the SGX provisioning device.";
type = types.str;
default = defaultGroup;
};
mode = mkOption {
description = "Mode to set for the SGX provisioning device.";
type = types.str;
default = "0660";
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = hasAttr cfg.user config.users.users;
message = "Given user does not exist";
}
{
assertion = (cfg.group == defaultGroup) || (hasAttr cfg.group config.users.groups);
message = "Given group does not exist";
}
];
users.groups = optionalAttrs (cfg.group == defaultGroup) {
"${cfg.group}" = { };
};
services.udev.extraRules = ''
SUBSYSTEM=="misc", KERNEL=="sgx_provision", OWNER="${cfg.user}", GROUP="${cfg.group}", MODE="${cfg.mode}"
'';
};
}

View file

@ -0,0 +1,37 @@
{ config, lib, pkgs, ... }:
with lib;
let
# gnupg's manual describes how to setup ccid udev rules:
# https://www.gnupg.org/howtos/card-howto/en/ch02s03.html
# gnupg folks advised me (https://dev.gnupg.org/T5409) to look at debian's rules:
# https://salsa.debian.org/debian/gnupg2/-/blob/debian/main/debian/scdaemon.udev
# the latest rev of the entire debian gnupg2 repo as of 2021-04-28
# the scdaemon.udev file was last commited on 2021-01-05 (7817a03):
scdaemonUdevRev = "01898735a015541e3ffb43c7245ac1e612f40836";
scdaemonRules = pkgs.fetchurl {
url = "https://salsa.debian.org/debian/gnupg2/-/raw/${scdaemonUdevRev}/debian/scdaemon.udev";
sha256 = "08v0vp6950bz7galvc92zdss89y9vcwbinmbfcdldy8x72w6rqr3";
};
# per debian's udev deb hook (https://man7.org/linux/man-pages/man1/dh_installudev.1.html)
destination = "60-scdaemon.rules";
scdaemonUdevRulesPkg = pkgs.runCommandNoCC "scdaemon-udev-rules" {} ''
loc="$out/lib/udev/rules.d/"
mkdir -p "''${loc}"
cp "${scdaemonRules}" "''${loc}/${destination}"
'';
cfg = config.hardware.gpgSmartcards;
in {
options.hardware.gpgSmartcards = {
enable = mkEnableOption "udev rules for gnupg smart cards";
};
config = mkIf cfg.enable {
services.udev.packages = [ scdaemonUdevRulesPkg ];
};
}

View file

@ -5,7 +5,6 @@ let
cfg = config.hardware.keyboard.zsa;
in
{
# TODO: make group configurable like in https://github.com/NixOS/nixpkgs/blob/0b2b4b8c4e729535a61db56468809c5c2d3d175c/pkgs/tools/security/nitrokey-app/udev-rules.nix ?
options.hardware.keyboard.zsa = {
enable = mkOption {
type = types.bool;
@ -14,7 +13,6 @@ in
Enables udev rules for keyboards from ZSA like the ErgoDox EZ, Planck EZ and Moonlander Mark I.
You need it when you want to flash a new configuration on the keyboard
or use their live training in the browser.
Access to the keyboard is granted to users in the "plugdev" group.
You may want to install the wally-cli package.
'';
};
@ -22,6 +20,5 @@ in
config = mkIf cfg.enable {
services.udev.packages = [ pkgs.zsa-udev-rules ];
users.groups.plugdev = {};
};
}

View file

@ -35,6 +35,7 @@ in
config = mkOption {
default = null;
type = types.nullOr types.path;
description = ''
Path to the configuration file which maps the memory, IRQs
and ports used by the PCMCIA hardware.

View file

@ -1,8 +1,9 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkOption mkEnableOption types mkIf mkMerge optional versionOlder;
inherit (lib) literalExpression mkOption mkEnableOption types mkIf mkMerge optional versionOlder;
cfg = config.hardware.system76;
opt = options.hardware.system76;
kpkgs = config.boot.kernelPackages;
modules = [ "system76" "system76-io" ] ++ (optional (versionOlder kpkgs.kernel.version "5.5") "system76-acpi");
@ -60,6 +61,7 @@ in {
firmware-daemon.enable = mkOption {
default = cfg.enableAll;
defaultText = literalExpression "config.${opt.enableAll}";
example = true;
description = "Whether to enable the system76 firmware daemon";
type = types.bool;
@ -67,6 +69,7 @@ in {
kernel-modules.enable = mkOption {
default = cfg.enableAll;
defaultText = literalExpression "config.${opt.enableAll}";
example = true;
description = "Whether to make the system76 out-of-tree kernel modules available";
type = types.bool;
@ -74,6 +77,7 @@ in {
power-daemon.enable = mkOption {
default = cfg.enableAll;
defaultText = literalExpression "config.${opt.enableAll}";
example = true;
description = "Whether to enable the system76 power daemon";
type = types.bool;

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs, baseModules, extraModules, modules, modulesPath, ... }:
{ config, lib, pkgs, extendModules, noUserModules, ... }:
with lib;
@ -6,11 +6,8 @@ let
cfg = config.documentation;
manualModules =
baseModules
# Modules for which to show options even when not imported
++ [ ../virtualisation/qemu-vm.nix ]
++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
/* Modules for which to show options even when not imported. */
extraDocModules = [ ../virtualisation/qemu-vm.nix ];
/* For the purpose of generating docs, evaluate options with each derivation
in `pkgs` (recursively) replaced by a fake with path "\${pkgs.attribute.path}".
@ -24,13 +21,10 @@ let
extraSources = cfg.nixos.extraModuleSources;
options =
let
scrubbedEval = evalModules {
modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ manualModules;
args = (config._module.args) // { modules = [ ]; };
specialArgs = {
pkgs = scrubDerivations "pkgs" pkgs;
inherit modulesPath;
};
extendNixOS = if cfg.nixos.includeAllModules then extendModules else noUserModules.extendModules;
scrubbedEval = extendNixOS {
modules = extraDocModules;
specialArgs.pkgs = scrubDerivations "pkgs" pkgs;
};
scrubDerivations = namePrefix: pkgSet: mapAttrs
(name: value:

View file

@ -1,7 +1,7 @@
{ pkgs, ... }:
{ lib, config, pkgs, ... }:
{
_module.args = {
utils = import ../../lib/utils.nix pkgs;
utils = import ../../lib/utils.nix { inherit lib config pkgs; };
};
}

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.system.nixos;
opt = options.system.nixos;
in
{
@ -53,6 +54,7 @@ in
stateVersion = mkOption {
type = types.str;
default = cfg.release;
defaultText = literalExpression "config.${opt.release}";
description = ''
Every once in a while, a new NixOS release may change
configuration defaults in a way incompatible with stateful

View file

@ -45,11 +45,13 @@
./hardware/ckb-next.nix
./hardware/cpu/amd-microcode.nix
./hardware/cpu/intel-microcode.nix
./hardware/cpu/intel-sgx.nix
./hardware/corectrl.nix
./hardware/digitalbitbox.nix
./hardware/device-tree.nix
./hardware/gkraken.nix
./hardware/flirc.nix
./hardware/gpgsmartcards.nix
./hardware/i2c.nix
./hardware/sensor/hddtemp.nix
./hardware/sensor/iio.nix
@ -446,6 +448,7 @@
./services/hardware/xow.nix
./services/logging/SystemdJournal2Gelf.nix
./services/logging/awstats.nix
./services/logging/filebeat.nix
./services/logging/fluentd.nix
./services/logging/graylog.nix
./services/logging/heartbeat.nix
@ -467,6 +470,7 @@
./services/mail/dovecot.nix
./services/mail/dspam.nix
./services/mail/exim.nix
./services/mail/maddy.nix
./services/mail/mail.nix
./services/mail/mailcatcher.nix
./services/mail/mailhog.nix
@ -926,6 +930,7 @@
./services/search/kibana.nix
./services/search/meilisearch.nix
./services/search/solr.nix
./services/security/aesmd.nix
./services/security/certmgr.nix
./services/security/cfssl.nix
./services/security/clamav.nix
@ -1021,6 +1026,7 @@
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/pgpkeyserver-lite.nix
./services/web-apps/powerdns-admin.nix
./services/web-apps/matomo.nix
./services/web-apps/moinmoin.nix
./services/web-apps/openwebrx.nix
@ -1191,8 +1197,7 @@
./virtualisation/kvmgt.nix
./virtualisation/openvswitch.nix
./virtualisation/parallels-guest.nix
./virtualisation/podman.nix
./virtualisation/podman-network-socket-ghostunnel.nix
./virtualisation/podman/default.nix
./virtualisation/qemu-guest-agent.nix
./virtualisation/railcar.nix
./virtualisation/spice-usb-redirection.nix

View file

@ -3,6 +3,18 @@
with lib;
let
cfg = config.programs.captive-browser;
browserDefault = chromium: concatStringsSep " " [
''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"''
''${chromium}/bin/chromium''
''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
''--proxy-server="socks5://$PROXY"''
''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"''
''--no-first-run''
''--new-window''
''--incognito''
''-no-default-browser-check''
''http://cache.nixos.org/''
];
in
{
###### interface
@ -26,18 +38,8 @@ in
# the options below are the same as in "captive-browser.toml"
browser = mkOption {
type = types.str;
default = concatStringsSep " " [
''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"''
''${pkgs.chromium}/bin/chromium''
''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
''--proxy-server="socks5://$PROXY"''
''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"''
''--no-first-run''
''--new-window''
''--incognito''
''-no-default-browser-check''
''http://cache.nixos.org/''
];
default = browserDefault pkgs.chromium;
defaultText = literalExpression (browserDefault "\${pkgs.chromium}");
description = ''
The shell (/bin/sh) command executed once the proxy starts.
When browser exits, the proxy exits. An extra env var PROXY is available.

View file

@ -60,7 +60,7 @@ in
environment.systemPackages = [ pkgs.dconf ];
# Needed for unwrapped applications
environment.sessionVariables.GIO_EXTRA_MODULES = mkIf cfg.enable [ "${pkgs.dconf.lib}/lib/gio/modules" ];
environment.variables.GIO_EXTRA_MODULES = mkIf cfg.enable [ "${pkgs.dconf.lib}/lib/gio/modules" ];
};
}

View file

@ -71,6 +71,7 @@ in
type = types.nullOr (types.enum pkgs.pinentry.flavors);
example = "gnome3";
default = defaultPinentryFlavor;
defaultText = literalDocBook ''matching the configured desktop environment'';
description = ''
Which pinentry interface to use. If not null, the path to the
pinentry binary will be passed to gpg-agent via commandline and

View file

@ -33,6 +33,13 @@ in
programs.ssh = {
enableAskPassword = mkOption {
type = types.bool;
default = config.services.xserver.enable;
defaultText = literalExpression "config.services.xserver.enable";
description = "Whether to configure SSH_ASKPASS in the environment.";
};
askPassword = mkOption {
type = types.str;
default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
@ -287,7 +294,7 @@ in
# Allow ssh-agent to ask for confirmation. This requires the
# unit to know about the user's $DISPLAY (via systemctl
# import-environment).
environment.SSH_ASKPASS = optionalString config.services.xserver.enable askPasswordWrapper;
environment.SSH_ASKPASS = optionalString cfg.enableAskPassword askPasswordWrapper;
environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS
};
@ -298,7 +305,7 @@ in
fi
'';
environment.variables.SSH_ASKPASS = optionalString config.services.xserver.enable askPassword;
environment.variables.SSH_ASKPASS = optionalString cfg.enableAskPassword askPassword;
};
}

View file

@ -1,6 +1,6 @@
# This module defines global configuration for the zshell.
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
@ -9,6 +9,7 @@ let
cfge = config.environment;
cfg = config.programs.zsh;
opt = options.programs.zsh;
zshAliases = concatStringsSep "\n" (
mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
@ -147,6 +148,7 @@ in
enableGlobalCompInit = mkOption {
default = cfg.enableCompletion;
defaultText = literalExpression "config.${opt.enableCompletion}";
description = ''
Enable execution of compinit call for all interactive zsh shells.

View file

@ -2,6 +2,7 @@
with lib;
let
cfg = config.security.acme;
opt = options.security.acme;
# Used to calculate timer accuracy for coalescing
numCerts = length (builtins.attrNames cfg.certs);
@ -163,9 +164,8 @@ let
[ "--dns" data.dnsProvider ]
++ optionals (!data.dnsPropagationCheck) [ "--dns.disable-cp" ]
++ optionals (data.dnsResolver != null) [ "--dns.resolvers" data.dnsResolver ]
) else (
[ "--http" "--http.webroot" data.webroot ]
);
) else if data.listenHTTP != null then [ "--http" "--http.port" data.listenHTTP ]
else [ "--http" "--http.webroot" data.webroot ];
commonOpts = [
"--accept-tos" # Checking the option is covered by the assertions
@ -321,11 +321,14 @@ let
}
fi
'');
} // optionalAttrs (data.listenHTTP != null && toInt (elemAt (splitString ":" data.listenHTTP) 1) < 1024) {
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
};
# Working directory will be /tmp
script = ''
set -euxo pipefail
${optionalString data.enableDebugLogs "set -x"}
set -euo pipefail
# This reimplements the expiration date check, but without querying
# the acme server first. By doing this offline, we avoid errors
@ -438,6 +441,8 @@ let
default = "_mkMergedOptionModule";
};
enableDebugLogs = mkEnableOption "debug logging for this certificate" // { default = cfg.enableDebugLogs; };
webroot = mkOption {
type = types.nullOr types.str;
default = null;
@ -451,6 +456,17 @@ let
'';
};
listenHTTP = mkOption {
type = types.nullOr types.str;
default = null;
example = ":1360";
description = ''
Interface and port to listen on to solve HTTP challenges
in the form [INTERFACE]:PORT.
If you use a port other than 80, you must proxy port 80 to this port.
'';
};
server = mkOption {
type = types.nullOr types.str;
default = null;
@ -470,6 +486,7 @@ let
email = mkOption {
type = types.nullOr types.str;
default = cfg.email;
defaultText = literalExpression "config.${opt.email}";
description = "Contact email address for the CA to be able to reach you.";
};
@ -616,6 +633,8 @@ in {
options = {
security.acme = {
enableDebugLogs = mkEnableOption "debug logging for all certificates by default" // { default = true; };
validMinDays = mkOption {
type = types.int;
default = 30;
@ -778,6 +797,28 @@ in {
`security.acme.certs.${cert}.webroot` are mutually exclusive.
'';
}
{
assertion = data.webroot == null || data.listenHTTP == null;
message = ''
Options `security.acme.certs.${cert}.webroot` and
`security.acme.certs.${cert}.listenHTTP` are mutually exclusive.
'';
}
{
assertion = data.listenHTTP == null || data.dnsProvider == null;
message = ''
Options `security.acme.certs.${cert}.listenHTTP` and
`security.acme.certs.${cert}.dnsProvider` are mutually exclusive.
'';
}
{
assertion = data.dnsProvider != null || data.webroot != null || data.listenHTTP != null;
message = ''
One of `security.acme.certs.${cert}.dnsProvider`,
`security.acme.certs.${cert}.webroot`, or
`security.acme.certs.${cert}.listenHTTP` must be provided.
'';
}
]) cfg.certs));
users.users.acme = {

View file

@ -1,8 +1,9 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkOption types;
inherit (lib) literalExpression mkOption types;
cfg = config.security.dhparams;
opt = options.security.dhparams;
bitType = types.addCheck types.int (b: b >= 16) // {
name = "bits";
@ -13,6 +14,7 @@ let
options.bits = mkOption {
type = bitType;
default = cfg.defaultBitSize;
defaultText = literalExpression "config.${opt.defaultBitSize}";
description = ''
The bit size for the prime that is used during a Diffie-Hellman
key exchange.

View file

@ -295,9 +295,14 @@ let
};
limits = mkOption {
default = [];
type = limitsType;
description = ''
Attribute set describing resource limits. Defaults to the
value of <option>security.pam.loginLimits</option>.
The meaning of the values is explained in <citerefentry>
<refentrytitle>limits.conf</refentrytitle><manvolnum>5</manvolnum>
</citerefentry>.
'';
};
@ -648,6 +653,51 @@ let
"${domain} ${type} ${item} ${toString value}\n")
limits);
limitsType = with lib.types; listOf (submodule ({ ... }: {
options = {
domain = mkOption {
description = "Username, groupname, or wildcard this limit applies to";
example = "@wheel";
type = str;
};
type = mkOption {
description = "Type of this limit";
type = enum [ "-" "hard" "soft" ];
default = "-";
};
item = mkOption {
description = "Item this limit applies to";
type = enum [
"core"
"data"
"fsize"
"memlock"
"nofile"
"rss"
"stack"
"cpu"
"nproc"
"as"
"maxlogins"
"maxsyslogins"
"priority"
"locks"
"sigpending"
"msgqueue"
"nice"
"rtprio"
];
};
value = mkOption {
description = "Value of this limit";
type = oneOf [ str int ];
};
};
}));
motd = pkgs.writeText "motd" config.users.motd;
makePAMService = name: service:
@ -669,6 +719,7 @@ in
security.pam.loginLimits = mkOption {
default = [];
type = limitsType;
example =
[ { domain = "ftp";
type = "hard";
@ -688,7 +739,8 @@ in
<varname>domain</varname>, <varname>type</varname>,
<varname>item</varname>, and <varname>value</varname>
attribute. The syntax and semantics of these attributes
must be that described in the limits.conf(5) man page.
must be that described in <citerefentry><refentrytitle>limits.conf</refentrytitle>
<manvolnum>5</manvolnum></citerefentry>.
Note that these limits do not apply to systemd services,
whose limits can be changed via <option>systemd.extraConfig</option>

View file

@ -1,11 +1,9 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, utils, ... }:
let
toplevelConfig = config;
inherit (lib) types;
inherit (import ../system/boot/systemd-lib.nix {
inherit config pkgs lib;
}) mkPathSafeName;
inherit (utils.systemdUtils.lib) mkPathSafeName;
in {
options.systemd.services = lib.mkOption {
type = types.attrsOf (types.submodule ({ name, config, ... }: {

View file

@ -1,10 +1,11 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.mpdscribble;
mpdCfg = config.services.mpd;
mpdOpt = options.services.mpd;
endpointUrls = {
"last.fm" = "http://post.audioscrobbler.com";
@ -108,6 +109,11 @@ in {
mpdCfg.network.listenAddress
else
"localhost");
defaultText = literalExpression ''
if config.${mpdOpt.network.listenAddress} != "any"
then config.${mpdOpt.network.listenAddress}
else "localhost"
'';
type = types.str;
description = ''
Host for the mpdscribble daemon to search for a mpd daemon on.
@ -122,6 +128,10 @@ in {
mpdCfg.credentials).passwordFile
else
null;
defaultText = literalDocBook ''
The first password file with read access configured for MPD when using a local instance,
otherwise <literal>null</literal>.
'';
type = types.nullOr types.str;
description = ''
File containing the password for the mpd daemon.
@ -132,6 +142,7 @@ in {
port = mkOption {
default = mpdCfg.network.port;
defaultText = literalExpression "config.${mpdOpt.network.port}";
type = types.port;
description = ''
Port for the mpdscribble daemon to search for a mpd daemon on.

View file

@ -54,12 +54,12 @@ let
# tcp json rpc
++ [ "--tcp.enabled ${toString cfg.tcp.enable}" ]
++ optionals cfg.tcp.enable [
"--tcp.address ${cfg.tcp.listenAddress}"
"--tcp.bind_to_address ${cfg.tcp.listenAddress}"
"--tcp.port ${toString cfg.tcp.port}" ]
# http json rpc
++ [ "--http.enabled ${toString cfg.http.enable}" ]
++ optionals cfg.http.enable [
"--http.address ${cfg.http.listenAddress}"
"--http.bind_to_address ${cfg.http.listenAddress}"
"--http.port ${toString cfg.http.port}"
] ++ optional (cfg.http.docRoot != null) "--http.doc_root \"${toString cfg.http.docRoot}\"");

View file

@ -1,10 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, utils, ... }:
with lib;
let
# Type for a valid systemd unit option. Needed for correctly passing "timerConfig" to "systemd.timers"
unitOption = (import ../../system/boot/systemd-unit-options.nix { inherit config lib; }).unitOption;
inherit (utils.systemdUtils.unitOptions) unitOption;
in
{
options.services.restic.backups = mkOption {

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, utils, ... }:
{ config, lib, options, pkgs, utils, ... }:
with lib;
let
gcfg = config.services.tarsnap;
opt = options.services.tarsnap;
configFile = name: cfg: ''
keyfile ${cfg.keyfile}
@ -59,12 +60,13 @@ in
};
archives = mkOption {
type = types.attrsOf (types.submodule ({ config, ... }:
type = types.attrsOf (types.submodule ({ config, options, ... }:
{
options = {
keyfile = mkOption {
type = types.str;
default = gcfg.keyfile;
defaultText = literalExpression "config.${opt.keyfile}";
description = ''
Set a specific keyfile for this archive. This defaults to
<literal>"/root/tarsnap.key"</literal> if left unspecified.
@ -87,6 +89,9 @@ in
cachedir = mkOption {
type = types.nullOr types.path;
default = "/var/cache/tarsnap/${utils.escapeSystemdPath config.keyfile}";
defaultText = literalExpression ''
"/var/cache/tarsnap/''${utils.escapeSystemdPath config.${options.keyfile}}"
'';
description = ''
The cache allows tarsnap to identify previously stored data
blocks, reducing archival time and bandwidth usage.
@ -320,21 +325,22 @@ in
${optionalString cfg.explicitSymlinks "-H"} \
${optionalString cfg.followSymlinks "-L"} \
${concatStringsSep " " cfg.directories}'';
cachedir = escapeShellArg cfg.cachedir;
in if (cfg.cachedir != null) then ''
mkdir -p ${cfg.cachedir}
chmod 0700 ${cfg.cachedir}
mkdir -p ${cachedir}
chmod 0700 ${cachedir}
( flock 9
if [ ! -e ${cfg.cachedir}/firstrun ]; then
if [ ! -e ${cachedir}/firstrun ]; then
( flock 10
flock -u 9
${tarsnap} --fsck
flock 9
) 10>${cfg.cachedir}/firstrun
) 10>${cachedir}/firstrun
fi
) 9>${cfg.cachedir}/lockf
) 9>${cachedir}/lockf
exec flock ${cfg.cachedir}/firstrun ${run}
exec flock ${cachedir}/firstrun ${run}
'' else "exec ${run}";
serviceConfig = {
@ -356,22 +362,23 @@ in
tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"'';
lastArchive = "$(${tarsnap} --list-archives | sort | tail -1)";
run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}'';
cachedir = escapeShellArg cfg.cachedir;
in if (cfg.cachedir != null) then ''
mkdir -p ${cfg.cachedir}
chmod 0700 ${cfg.cachedir}
mkdir -p ${cachedir}
chmod 0700 ${cachedir}
( flock 9
if [ ! -e ${cfg.cachedir}/firstrun ]; then
if [ ! -e ${cachedir}/firstrun ]; then
( flock 10
flock -u 9
${tarsnap} --fsck
flock 9
) 10>${cfg.cachedir}/firstrun
) 10>${cachedir}/firstrun
fi
) 9>${cfg.cachedir}/lockf
) 9>${cachedir}/lockf
exec flock ${cfg.cachedir}/firstrun ${run}
exec flock ${cachedir}/firstrun ${run}
'' else "exec ${run}";
serviceConfig = {

View file

@ -1,6 +1,7 @@
{ config, lib, pkgs, ...}:
{ config, lib, options, pkgs, ...}:
let
cfg = config.services.hadoop;
opt = options.services.hadoop;
in
with lib;
{
@ -44,6 +45,14 @@ with lib;
"mapreduce.map.env" = "HADOOP_MAPRED_HOME=${cfg.package}/lib/${cfg.package.untarDir}";
"mapreduce.reduce.env" = "HADOOP_MAPRED_HOME=${cfg.package}/lib/${cfg.package.untarDir}";
};
defaultText = literalExpression ''
{
"mapreduce.framework.name" = "yarn";
"yarn.app.mapreduce.am.env" = "HADOOP_MAPRED_HOME=''${config.${opt.package}}/lib/''${config.${opt.package}.untarDir}";
"mapreduce.map.env" = "HADOOP_MAPRED_HOME=''${config.${opt.package}}/lib/''${config.${opt.package}.untarDir}";
"mapreduce.reduce.env" = "HADOOP_MAPRED_HOME=''${config.${opt.package}}/lib/''${config.${opt.package}.untarDir}";
}
'';
type = types.attrsOf types.anything;
example = literalExpression ''
options.services.hadoop.mapredSite.default // {
@ -98,6 +107,9 @@ with lib;
log4jProperties = mkOption {
default = "${cfg.package}/lib/${cfg.package.untarDir}/etc/hadoop/log4j.properties";
defaultText = literalExpression ''
"''${config.${opt.package}}/lib/''${config.${opt.package}.untarDir}/etc/hadoop/log4j.properties"
'';
type = types.path;
example = literalExpression ''
"''${pkgs.hadoop}/lib/''${pkgs.hadoop.untarDir}/etc/hadoop/log4j.properties";

View file

@ -1,9 +1,10 @@
{ config, pkgs, lib, ... }:
{ config, options, pkgs, lib, ... }:
with lib;
let
cfg = config.services.kubernetes.addons.dashboard;
opt = options.services.kubernetes.addons.dashboard;
in {
imports = [
(mkRenamedOptionModule [ "services" "kubernetes" "addons" "dashboard" "enableRBAC" ] [ "services" "kubernetes" "addons" "dashboard" "rbac" "enable" ])
@ -28,6 +29,9 @@ in {
description = "Whether to enable role based access control is enabled for kubernetes dashboard";
type = types.bool;
default = elem "RBAC" config.services.kubernetes.apiserver.authorizationMode;
defaultText = literalExpression ''
elem "RBAC" config.${options.services.kubernetes.apiserver.authorizationMode}
'';
};
clusterAdmin = mkOption {
@ -54,6 +58,14 @@ in {
finalImageTag = cfg.version;
sha256 = "01xrr4pwgr2hcjrjsi3d14ifpzdfbxzqpzxbk2fkbjb9zkv38zxy";
};
defaultText = literalExpression ''
{
imageName = "k8s.gcr.io/kubernetes-dashboard-amd64";
imageDigest = "sha256:0ae6b69432e78069c5ce2bcde0fe409c5c4d6f0f4d9cd50a17974fea38898747";
finalImageTag = config.${opt.version};
sha256 = "01xrr4pwgr2hcjrjsi3d14ifpzdfbxzqpzxbk2fkbjb9zkv38zxy";
};
'';
};
};

View file

@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, options, pkgs, lib, ... }:
with lib;
@ -23,6 +23,10 @@ in {
take 3 (splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange
))
) + ".254";
defaultText = literalDocBook ''
The <literal>x.y.z.254</literal> IP of
<literal>config.${options.services.kubernetes.apiserver.serviceClusterIpRange}</literal>.
'';
type = types.str;
};

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
cfg = top.apiserver;
isRBACEnabled = elem "RBAC" cfg.authorizationMode;
@ -84,6 +85,7 @@ in
clientCaFile = mkOption {
description = "Kubernetes apiserver CA file for client auth.";
default = top.caFile;
defaultText = literalExpression "config.${otop.caFile}";
type = nullOr path;
};
@ -138,6 +140,7 @@ in
caFile = mkOption {
description = "Etcd ca file.";
default = top.caFile;
defaultText = literalExpression "config.${otop.caFile}";
type = types.nullOr types.path;
};
};
@ -157,6 +160,7 @@ in
featureGates = mkOption {
description = "List set of feature gates";
default = top.featureGates;
defaultText = literalExpression "config.${otop.featureGates}";
type = listOf str;
};
@ -175,6 +179,7 @@ in
kubeletClientCaFile = mkOption {
description = "Path to a cert file for connecting to kubelet.";
default = top.caFile;
defaultText = literalExpression "config.${otop.caFile}";
type = nullOr path;
};

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
cfg = top.controllerManager;
in
{
@ -30,6 +31,7 @@ in
clusterCidr = mkOption {
description = "Kubernetes CIDR Range for Pods in cluster.";
default = top.clusterCidr;
defaultText = literalExpression "config.${otop.clusterCidr}";
type = str;
};
@ -44,6 +46,7 @@ in
featureGates = mkOption {
description = "List set of feature gates";
default = top.featureGates;
defaultText = literalExpression "config.${otop.featureGates}";
type = listOf str;
};
@ -67,6 +70,7 @@ in
service account's token secret.
'';
default = top.caFile;
defaultText = literalExpression "config.${otop.caFile}";
type = nullOr path;
};

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.kubernetes;
opt = options.services.kubernetes;
defaultContainerdSettings = {
version = 2;
@ -87,6 +88,7 @@ let
description = "${prefix} certificate authority file used to connect to kube-apiserver.";
type = types.nullOr types.path;
default = cfg.caFile;
defaultText = literalExpression "config.${opt.caFile}";
};
certFile = mkOption {

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
cfg = top.kubelet;
cniConfig =
@ -35,6 +36,7 @@ let
key = mkOption {
description = "Key of taint.";
default = name;
defaultText = literalDocBook "Name of this submodule.";
type = str;
};
value = mkOption {
@ -76,12 +78,14 @@ in
clusterDomain = mkOption {
description = "Use alternative domain.";
default = config.services.kubernetes.addons.dns.clusterDomain;
defaultText = literalExpression "config.${options.services.kubernetes.addons.dns.clusterDomain}";
type = str;
};
clientCaFile = mkOption {
description = "Kubernetes apiserver CA file for client authentication.";
default = top.caFile;
defaultText = literalExpression "config.${otop.caFile}";
type = nullOr path;
};
@ -148,6 +152,7 @@ in
featureGates = mkOption {
description = "List set of feature gates";
default = top.featureGates;
defaultText = literalExpression "config.${otop.featureGates}";
type = listOf str;
};

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
cfg = top.proxy;
in
{
@ -31,6 +32,7 @@ in
featureGates = mkOption {
description = "List set of feature gates";
default = top.featureGates;
defaultText = literalExpression "config.${otop.featureGates}";
type = listOf str;
};

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
cfg = top.scheduler;
in
{
@ -27,6 +28,7 @@ in
featureGates = mkOption {
description = "List set of feature gates";
default = top.featureGates;
defaultText = literalExpression "config.${otop.featureGates}";
type = listOf str;
};

View file

@ -1,10 +1,11 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.slurm;
opt = options.services.slurm;
# configuration file can be generated by http://slurm.schedmd.com/configurator.html
defaultUser = "slurm";
@ -90,6 +91,7 @@ in
storageUser = mkOption {
type = types.str;
default = cfg.user;
defaultText = literalExpression "config.${opt.user}";
description = ''
Database user name.
'';
@ -154,6 +156,7 @@ in
controlAddr = mkOption {
type = types.nullOr types.str;
default = cfg.controlMachine;
defaultText = literalExpression "config.${opt.controlMachine}";
example = null;
description = ''
Name that ControlMachine should be referred to in establishing a
@ -279,6 +282,10 @@ in
type = types.path;
internal = true;
default = etcSlurm;
defaultText = literalDocBook ''
Directory created from generated config files and
<literal>config.${opt.extraConfigPaths}</literal>.
'';
description = ''
Path to directory with slurm config files. This option is set by default from the
Slurm module and is meant to make the Slurm config file available to other modules.

View file

@ -1,11 +1,12 @@
# NixOS module for Buildbot continous integration server.
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.buildbot-master;
opt = options.services.buildbot-master;
python = cfg.package.pythonModule;
@ -152,6 +153,7 @@ in {
buildbotDir = mkOption {
default = "${cfg.home}/master";
defaultText = literalExpression ''"''${config.${opt.home}}/master"'';
type = types.path;
description = "Specifies the Buildbot directory.";
};

View file

@ -1,11 +1,12 @@
# NixOS module for Buildbot Worker.
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.buildbot-worker;
opt = options.services.buildbot-worker;
python = cfg.package.pythonModule;
@ -77,6 +78,7 @@ in {
buildbotDir = mkOption {
default = "${cfg.home}/worker";
defaultText = literalExpression ''"''${config.${opt.home}}/worker"'';
type = types.path;
description = "Specifies the Buildbot directory.";
};

View file

@ -10,6 +10,8 @@ let
stateDir = "%S/${systemdDir}";
# %L: Log directory root (usually /var/log); see systemd.unit(5)
logsDir = "%L/${systemdDir}";
# Name of file stored in service state directory
currentConfigTokenFilename = ".current-token";
in
{
options.services.github-runner = {
@ -144,13 +146,11 @@ in
ExecStart = "${cfg.package}/bin/runsvc.sh";
# Does the following, sequentially:
# - Copy the current and the previous `tokenFile` to the $RUNTIME_DIRECTORY
# and make it accessible to the service user to allow for a content
# comparison.
# - If the module configuration or the token has changed, clear the state directory.
# - Configure the runner.
# - Copy the configured `tokenFile` to the $STATE_DIRECTORY and make it
# inaccessible to the service user.
# - If the module configuration or the token has changed, purge the state directory,
# and create the current and the new token file with the contents of the configured
# token. While both files have the same content, only the later is accessible by
# the service user.
# - Configure the runner using the new token file. When finished, delete it.
# - Set up the directory structure by creating the necessary symlinks.
ExecStartPre =
let
@ -173,37 +173,20 @@ in
currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json";
runnerRegistrationConfig = getAttrs [ "name" "tokenFile" "url" "runnerGroup" "extraLabels" ] cfg;
newConfigPath = builtins.toFile "${svcName}-config.json" (builtins.toJSON runnerRegistrationConfig);
currentConfigTokenFilename = ".current-token";
newConfigTokenFilename = ".new-token";
runnerCredFiles = [
".credentials"
".credentials_rsaparams"
".runner"
];
ownConfigTokens = writeScript "own-config-tokens" ''
# Copy current and new token file to runtime dir and make it accessible to the service user
cp ${escapeShellArg cfg.tokenFile} "$RUNTIME_DIRECTORY/${newConfigTokenFilename}"
chmod 600 "$RUNTIME_DIRECTORY/${newConfigTokenFilename}"
chown "$USER" "$RUNTIME_DIRECTORY/${newConfigTokenFilename}"
if [[ -e "$STATE_DIRECTORY/${currentConfigTokenFilename}" ]]; then
cp "$STATE_DIRECTORY/${currentConfigTokenFilename}" "$RUNTIME_DIRECTORY/${currentConfigTokenFilename}"
chmod 600 "$RUNTIME_DIRECTORY/${currentConfigTokenFilename}"
chown "$USER" "$RUNTIME_DIRECTORY/${currentConfigTokenFilename}"
fi
'';
disownConfigTokens = writeScript "disown-config-tokens" ''
# Make the token inaccessible to the runner service user
chmod 600 "$STATE_DIRECTORY/${currentConfigTokenFilename}"
chown root:root "$STATE_DIRECTORY/${currentConfigTokenFilename}"
'';
unconfigureRunner = writeScript "unconfigure" ''
differs=
# Set `differs = 1` if current and new runner config differ or if `currentConfigPath` does not exist
${pkgs.diffutils}/bin/diff -q '${newConfigPath}' "${currentConfigPath}" >/dev/null 2>&1 || differs=1
# Also trigger a registration if the token content changed
${pkgs.diffutils}/bin/diff -q \
"$RUNTIME_DIRECTORY"/{${currentConfigTokenFilename},${newConfigTokenFilename}} \
"$STATE_DIRECTORY"/${currentConfigTokenFilename} \
${escapeShellArg cfg.tokenFile} \
>/dev/null 2>&1 || differs=1
if [[ -n "$differs" ]]; then
@ -211,13 +194,18 @@ in
echo "The old runner will still appear in the GitHub Actions UI." \
"You have to remove it manually."
find "$STATE_DIRECTORY/" -mindepth 1 -delete
# Copy the configured token file to the state dir and allow the service user to read the file
install --mode=666 ${escapeShellArg cfg.tokenFile} "$STATE_DIRECTORY/${newConfigTokenFilename}"
# Also copy current file to allow for a diff on the next start
install --mode=600 ${escapeShellArg cfg.tokenFile} "$STATE_DIRECTORY/${currentConfigTokenFilename}"
fi
'';
configureRunner = writeScript "configure" ''
empty=$(ls -A "$STATE_DIRECTORY")
if [[ -z "$empty" ]]; then
if [[ -e "$STATE_DIRECTORY/${newConfigTokenFilename}" ]]; then
echo "Configuring GitHub Actions Runner"
token=$(< "$RUNTIME_DIRECTORY"/${newConfigTokenFilename})
token=$(< "$STATE_DIRECTORY"/${newConfigTokenFilename})
RUNNER_ROOT="$STATE_DIRECTORY" ${cfg.package}/bin/config.sh \
--unattended \
--work "$RUNTIME_DIRECTORY" \
@ -234,8 +222,7 @@ in
rm -rf "$STATE_DIRECTORY/_diag/"
# Cleanup token from config
rm -f "$RUNTIME_DIRECTORY"/${currentConfigTokenFilename}
mv "$RUNTIME_DIRECTORY"/${newConfigTokenFilename} "$STATE_DIRECTORY/${currentConfigTokenFilename}"
rm "$STATE_DIRECTORY/${newConfigTokenFilename}"
# Symlink to new config
ln -s '${newConfigPath}' "${currentConfigPath}"
@ -250,10 +237,8 @@ in
'';
in
map (x: "${x} ${escapeShellArgs [ stateDir runtimeDir logsDir ]}") [
"+${ownConfigTokens}" # runs as root
unconfigureRunner
"+${unconfigureRunner}" # runs as root
configureRunner
"+${disownConfigTokens}" # runs as root
setupRuntimeDir
];
@ -266,6 +251,13 @@ in
StateDirectoryMode = "0700";
WorkingDirectory = runtimeDir;
InaccessiblePaths = [
# Token file path given in the configuration
cfg.tokenFile
# Token file in the state directory
"${stateDir}/${currentConfigTokenFilename}"
];
# By default, use a dynamically allocated user
DynamicUser = true;

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.gocd-agent;
opt = options.services.gocd-agent;
in {
options = {
services.gocd-agent = {
@ -98,6 +99,15 @@ in {
"-Dcruise.console.publish.interval=10"
"-Djava.security.egd=file:/dev/./urandom"
];
defaultText = literalExpression ''
[
"-Xms''${config.${opt.initialJavaHeapSize}}"
"-Xmx''${config.${opt.maxJavaHeapMemory}}"
"-Djava.io.tmpdir=/tmp"
"-Dcruise.console.publish.interval=10"
"-Djava.security.egd=file:/dev/./urandom"
]
'';
description = ''
Specifies startup command line arguments to pass to Go.CD agent
java process.

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.gocd-server;
opt = options.services.gocd-server;
in {
options = {
services.gocd-server = {
@ -106,6 +107,20 @@ in {
"-Dcruise.server.port=${toString cfg.port}"
"-Dcruise.server.ssl.port=${toString cfg.sslPort}"
];
defaultText = literalExpression ''
[
"-Xms''${config.${opt.initialJavaHeapSize}}"
"-Xmx''${config.${opt.maxJavaHeapMemory}}"
"-Dcruise.listen.host=''${config.${opt.listenAddress}}"
"-Duser.language=en"
"-Djruby.rack.request.size.threshold.bytes=30000000"
"-Duser.country=US"
"-Dcruise.config.dir=''${config.${opt.workDir}}/conf"
"-Dcruise.config.file=''${config.${opt.workDir}}/conf/cruise-config.xml"
"-Dcruise.server.port=''${toString config.${opt.port}}"
"-Dcruise.server.ssl.port=''${toString config.${opt.sslPort}}"
]
'';
description = ''
Specifies startup command line arguments to pass to Go.CD server

View file

@ -43,8 +43,8 @@ in {
package = mkOption {
type = types.package;
default = pkgs.couchdb;
defaultText = literalExpression "pkgs.couchdb";
default = pkgs.couchdb3;
defaultText = literalExpression "pkgs.couchdb3";
description = ''
CouchDB package to use.
'';
@ -150,6 +150,14 @@ in {
'';
};
argsFile = mkOption {
type = types.path;
default = "${cfg.package}/etc/vm.args";
description = ''
vm.args configuration. Overrides Couchdb's Erlang VM parameters file.
'';
};
configFile = mkOption {
type = types.path;
description = ''
@ -186,12 +194,14 @@ in {
'';
environment = {
# we are actually specifying 4 configuration files:
# we are actually specifying 5 configuration files:
# 1. the preinstalled default.ini
# 2. the module configuration
# 3. the extraConfig from the module options
# 4. the locally writable config file, which couchdb itself writes to
ERL_FLAGS= ''-couch_ini ${cfg.package}/etc/default.ini ${configFile} ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig} ${cfg.configFile}'';
# 5. the vm.args file
COUCHDB_ARGS_FILE=''${cfg.argsFile}'';
};
serviceConfig = {

View file

@ -1,14 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, options, lib, pkgs, ... }:
with lib;
let
cfg = config.services.hbase;
defaultConfig = {
"hbase.rootdir" = "file://${cfg.dataDir}/hbase";
"hbase.zookeeper.property.dataDir" = "${cfg.dataDir}/zookeeper";
};
opt = options.services.hbase;
buildProperty = configAttr:
(builtins.concatStringsSep "\n"
@ -23,7 +19,7 @@ let
configFile = pkgs.writeText "hbase-site.xml"
''<configuration>
${buildProperty (defaultConfig // cfg.settings)}
${buildProperty (opt.settings.default // cfg.settings)}
</configuration>
'';
@ -96,7 +92,16 @@ in {
settings = mkOption {
type = with lib.types; attrsOf (oneOf [ str int bool ]);
default = defaultConfig;
default = {
"hbase.rootdir" = "file://${cfg.dataDir}/hbase";
"hbase.zookeeper.property.dataDir" = "${cfg.dataDir}/zookeeper";
};
defaultText = literalExpression ''
{
"hbase.rootdir" = "file://''${config.${opt.dataDir}}/hbase";
"hbase.zookeeper.property.dataDir" = "''${config.${opt.dataDir}}/zookeeper";
}
'';
description = ''
configurations in hbase-site.xml, see <link xlink:href="https://github.com/apache/hbase/blob/master/hbase-server/src/test/resources/hbase-site.xml"/> for details.
'';

View file

@ -10,7 +10,7 @@ in
services.influxdb2 = {
enable = mkEnableOption "the influxdb2 server";
package = mkOption {
default = pkgs.influxdb2;
default = pkgs.influxdb2-server;
defaultText = literalExpression "pkgs.influxdb2";
description = "influxdb2 derivation to use.";
type = types.package;

View file

@ -4,6 +4,7 @@ with lib;
let
cfg = config.services.neo4j;
opt = options.services.neo4j;
certDirOpt = options.services.neo4j.directories.certificates;
isDefaultPathOption = opt: isOption opt && opt.type == types.path && opt.highestPrio >= 1500;
@ -256,6 +257,7 @@ in {
certificates = mkOption {
type = types.path;
default = "${cfg.directories.home}/certificates";
defaultText = literalExpression ''"''${config.${opt.directories.home}}/certificates"'';
description = ''
Directory for storing certificates to be used by Neo4j for
TLS connections.
@ -280,6 +282,7 @@ in {
data = mkOption {
type = types.path;
default = "${cfg.directories.home}/data";
defaultText = literalExpression ''"''${config.${opt.directories.home}}/data"'';
description = ''
Path of the data directory. You must not configure more than one
Neo4j installation to use the same data directory.
@ -305,6 +308,7 @@ in {
imports = mkOption {
type = types.path;
default = "${cfg.directories.home}/import";
defaultText = literalExpression ''"''${config.${opt.directories.home}}/import"'';
description = ''
The root directory for file URLs used with the Cypher
<literal>LOAD CSV</literal> clause. Only meaningful when
@ -321,6 +325,7 @@ in {
plugins = mkOption {
type = types.path;
default = "${cfg.directories.home}/plugins";
defaultText = literalExpression ''"''${config.${opt.directories.home}}/plugins"'';
description = ''
Path of the database plugin directory. Compiled Java JAR files that
contain database procedures will be loaded if they are placed in
@ -432,6 +437,7 @@ in {
baseDirectory = mkOption {
type = types.path;
default = "${cfg.directories.certificates}/${name}";
defaultText = literalExpression ''"''${config.${opt.directories.certificates}}/''${name}"'';
description = ''
The mandatory base directory for cryptographic objects of this
policy. This path is only automatically generated when this
@ -493,6 +499,7 @@ in {
revokedDir = mkOption {
type = types.path;
default = "${config.baseDirectory}/revoked";
defaultText = literalExpression ''"''${config.${options.baseDirectory}}/revoked"'';
description = ''
Path to directory of CRLs (Certificate Revocation Lists) in
PEM format. Must be an absolute path. The existence of this
@ -528,6 +535,7 @@ in {
trustedDir = mkOption {
type = types.path;
default = "${config.baseDirectory}/trusted";
defaultText = literalExpression ''"''${config.${options.baseDirectory}}/trusted"'';
description = ''
Path to directory of X.509 certificates in PEM format for
trusted parties. Must be an absolute path. The existence of this

View file

@ -289,14 +289,16 @@ in
port = cfg.port;
};
services.postgresql.package =
services.postgresql.package = let
mkThrow = ver: throw "postgresql_${ver} was removed, please upgrade your postgresql version.";
in
# Note: when changing the default, make it conditional on
# system.stateVersion to maintain compatibility with existing
# systems!
mkDefault (if versionAtLeast config.system.stateVersion "21.11" then pkgs.postgresql_13
else if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
else if versionAtLeast config.system.stateVersion "17.09" then pkgs.postgresql_9_6
else throw "postgresql_9_5 was removed, please upgrade your postgresql version.");
else if versionAtLeast config.system.stateVersion "17.09" then mkThrow "9_6"
else mkThrow "9_5");
services.postgresql.dataDir = mkDefault "/var/lib/postgresql/${cfg.package.psqlSchema}";

View file

@ -52,37 +52,51 @@ Type "help" for help.
<section xml:id="module-services-postgres-upgrading">
<title>Upgrading</title>
<note>
<para>
The steps below demonstrate how to upgrade from an older version to <package>pkgs.postgresql_13</package>.
These instructions are also applicable to other versions.
</para>
</note>
<para>
Major PostgreSQL upgrade requires PostgreSQL downtime and a few imperative steps to be called. To simplify this process, use the following NixOS module:
Major PostgreSQL upgrades require a downtime and a few imperative steps to be called. This is the case because
each major version has some internal changes in the databases' state during major releases. Because of that,
NixOS places the state into <filename>/var/lib/postgresql/&lt;version&gt;</filename> where each <literal>version</literal>
can be obtained like this:
<programlisting>
containers.temp-pg.config.services.postgresql = {
enable = true;
package = pkgs.postgresql_12;
## set a custom new dataDir
# dataDir = "/some/data/dir";
};
environment.systemPackages =
let newpg = config.containers.temp-pg.config.services.postgresql;
in [
(pkgs.writeScriptBin "upgrade-pg-cluster" ''
set -x
export OLDDATA="${config.services.postgresql.dataDir}"
export NEWDATA="${newpg.dataDir}"
export OLDBIN="${config.services.postgresql.package}/bin"
export NEWBIN="${newpg.package}/bin"
<prompt>$ </prompt>nix-instantiate --eval -A postgresql_13.psqlSchema
"13"
</programlisting>
For an upgrade, a script like this can be used to simplify the process:
<programlisting>
{ config, pkgs, ... }:
{
<xref linkend="opt-environment.systemPackages" /> = [
(pkgs.writeScriptBin "upgrade-pg-cluster" ''
set -eux
# XXX it's perhaps advisable to stop all services that depend on postgresql
systemctl stop postgresql
install -d -m 0700 -o postgres -g postgres "$NEWDATA"
cd "$NEWDATA"
sudo -u postgres $NEWBIN/initdb -D "$NEWDATA"
# XXX replace `&lt;new version&gt;` with the psqlSchema here
export NEWDATA="/var/lib/postgresql/&lt;new version&gt;"
systemctl stop postgresql # old one
# XXX specify the postgresql package you'd like to upgrade to
export NEWBIN="${pkgs.postgresql_13}/bin"
sudo -u postgres $NEWBIN/pg_upgrade \
--old-datadir "$OLDDATA" --new-datadir "$NEWDATA" \
--old-bindir $OLDBIN --new-bindir $NEWBIN \
"$@"
'')
];
export OLDDATA="${config.<xref linkend="opt-services.postgresql.dataDir"/>}"
export OLDBIN="${config.<xref linkend="opt-services.postgresql.package"/>}/bin"
install -d -m 0700 -o postgres -g postgres "$NEWDATA"
cd "$NEWDATA"
sudo -u postgres $NEWBIN/initdb -D "$NEWDATA"
sudo -u postgres $NEWBIN/pg_upgrade \
--old-datadir "$OLDDATA" --new-datadir "$NEWDATA" \
--old-bindir $OLDBIN --new-bindir $NEWBIN \
"$@"
'')
];
}
</programlisting>
</para>
@ -103,17 +117,25 @@ Type "help" for help.
</listitem>
<listitem>
<para>
Run <literal>upgrade-pg-cluster</literal>. It will stop old postgresql, initialize new one and migrate old one to new one. You may supply arguments like <literal>--jobs 4</literal> and <literal>--link</literal> to speedup migration process. See <link xlink:href="https://www.postgresql.org/docs/current/pgupgrade.html" /> for details.
Run <literal>upgrade-pg-cluster</literal>. It will stop old postgresql, initialize a new one and migrate the old one to the new one. You may supply arguments like <literal>--jobs 4</literal> and <literal>--link</literal> to speedup migration process. See <link xlink:href="https://www.postgresql.org/docs/current/pgupgrade.html" /> for details.
</para>
</listitem>
<listitem>
<para>
Change postgresql package in NixOS configuration to the one you were upgrading to, and change <literal>dataDir</literal> to the one you have migrated to. Rebuild NixOS. This should start new postgres using upgraded data directory.
Change postgresql package in NixOS configuration to the one you were upgrading to via <xref linkend="opt-services.postgresql.package" />. Rebuild NixOS. This should start new postgres using upgraded data directory and all services you stopped during the upgrade.
</para>
</listitem>
<listitem>
<para>
After upgrade you may want to <literal>ANALYZE</literal> new db.
After the upgrade it's advisable to analyze the new cluster (as <literal>su -l postgres</literal> in the
<xref linkend="opt-services.postgresql.dataDir" />, in this example <filename>/var/lib/postgresql/13</filename>):
<programlisting>
<prompt>$ </prompt>./analyze_new_cluster.sh
</programlisting>
<warning><para>The next step removes the old state-directory!</para></warning>
<programlisting>
<prompt>$ </prompt>./delete_old_cluster.sh
</programlisting>
</para>
</listitem>
</orderedlist>

View file

@ -5,17 +5,18 @@ with lib;
let
cfg = config.services.redis;
ulimitNofile = cfg.maxclients + 32;
mkValueString = value:
if value == true then "yes"
else if value == false then "no"
else generators.mkValueStringDefault { } value;
redisConfig = pkgs.writeText "redis.conf" (generators.toKeyValue {
redisConfig = settings: pkgs.writeText "redis.conf" (generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " ";
} cfg.settings);
} settings);
redisName = name: "redis" + optionalString (name != "") ("-"+name);
enabledServers = filterAttrs (name: conf: conf.enable) config.services.redis.servers;
in {
imports = [
@ -24,7 +25,28 @@ in {
(mkRemovedOptionModule [ "services" "redis" "dbFilename" ] "The redis module now uses /var/lib/redis/dump.rdb as database dump location.")
(mkRemovedOptionModule [ "services" "redis" "appendOnlyFilename" ] "This option was never used.")
(mkRemovedOptionModule [ "services" "redis" "pidFile" ] "This option was removed.")
(mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.settings instead.")
(mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.servers.*.settings instead.")
(mkRenamedOptionModule [ "services" "redis" "enable"] [ "services" "redis" "servers" "" "enable" ])
(mkRenamedOptionModule [ "services" "redis" "port"] [ "services" "redis" "servers" "" "port" ])
(mkRenamedOptionModule [ "services" "redis" "openFirewall"] [ "services" "redis" "servers" "" "openFirewall" ])
(mkRenamedOptionModule [ "services" "redis" "bind"] [ "services" "redis" "servers" "" "bind" ])
(mkRenamedOptionModule [ "services" "redis" "unixSocket"] [ "services" "redis" "servers" "" "unixSocket" ])
(mkRenamedOptionModule [ "services" "redis" "unixSocketPerm"] [ "services" "redis" "servers" "" "unixSocketPerm" ])
(mkRenamedOptionModule [ "services" "redis" "logLevel"] [ "services" "redis" "servers" "" "logLevel" ])
(mkRenamedOptionModule [ "services" "redis" "logfile"] [ "services" "redis" "servers" "" "logfile" ])
(mkRenamedOptionModule [ "services" "redis" "syslog"] [ "services" "redis" "servers" "" "syslog" ])
(mkRenamedOptionModule [ "services" "redis" "databases"] [ "services" "redis" "servers" "" "databases" ])
(mkRenamedOptionModule [ "services" "redis" "maxclients"] [ "services" "redis" "servers" "" "maxclients" ])
(mkRenamedOptionModule [ "services" "redis" "save"] [ "services" "redis" "servers" "" "save" ])
(mkRenamedOptionModule [ "services" "redis" "slaveOf"] [ "services" "redis" "servers" "" "slaveOf" ])
(mkRenamedOptionModule [ "services" "redis" "masterAuth"] [ "services" "redis" "servers" "" "masterAuth" ])
(mkRenamedOptionModule [ "services" "redis" "requirePass"] [ "services" "redis" "servers" "" "requirePass" ])
(mkRenamedOptionModule [ "services" "redis" "requirePassFile"] [ "services" "redis" "servers" "" "requirePassFile" ])
(mkRenamedOptionModule [ "services" "redis" "appendOnly"] [ "services" "redis" "servers" "" "appendOnly" ])
(mkRenamedOptionModule [ "services" "redis" "appendFsync"] [ "services" "redis" "servers" "" "appendFsync" ])
(mkRenamedOptionModule [ "services" "redis" "slowLogLogSlowerThan"] [ "services" "redis" "servers" "" "slowLogLogSlowerThan" ])
(mkRenamedOptionModule [ "services" "redis" "slowLogMaxLen"] [ "services" "redis" "servers" "" "slowLogMaxLen" ])
(mkRenamedOptionModule [ "services" "redis" "settings"] [ "services" "redis" "servers" "" "settings" ])
];
###### interface
@ -32,18 +54,6 @@ in {
options = {
services.redis = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable the Redis server. Note that the NixOS module for
Redis disables kernel support for Transparent Huge Pages (THP),
because this features causes major performance problems for Redis,
e.g. (https://redis.io/topics/latency).
'';
};
package = mkOption {
type = types.package;
default = pkgs.redis;
@ -51,176 +61,226 @@ in {
description = "Which Redis derivation to use.";
};
port = mkOption {
type = types.port;
default = 6379;
description = "The port for Redis to listen to.";
};
vmOverCommit = mkEnableOption ''
setting of vm.overcommit_memory to 1
(Suggested for Background Saving: http://redis.io/topics/faq)
'';
vmOverCommit = mkOption {
type = types.bool;
default = false;
description = ''
Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq)
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};
bind = mkOption {
type = with types; nullOr str;
default = "127.0.0.1";
description = ''
The IP interface to bind to.
<literal>null</literal> means "all interfaces".
'';
example = "192.0.2.1";
};
unixSocket = mkOption {
type = with types; nullOr path;
default = null;
description = "The path to the socket to bind to.";
example = "/run/redis/redis.sock";
};
unixSocketPerm = mkOption {
type = types.int;
default = 750;
description = "Change permissions for the socket";
example = 700;
};
logLevel = mkOption {
type = types.str;
default = "notice"; # debug, verbose, notice, warning
example = "debug";
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
};
logfile = mkOption {
type = types.str;
default = "/dev/null";
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
example = "/var/log/redis.log";
};
syslog = mkOption {
type = types.bool;
default = true;
description = "Enable logging to the system logger.";
};
databases = mkOption {
type = types.int;
default = 16;
description = "Set the number of databases.";
};
maxclients = mkOption {
type = types.int;
default = 10000;
description = "Set the max number of connected clients at the same time.";
};
save = mkOption {
type = with types; listOf (listOf int);
default = [ [900 1] [300 10] [60 10000] ];
description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
};
slaveOf = mkOption {
type = with types; nullOr (submodule ({ ... }: {
servers = mkOption {
type = with types; attrsOf (submodule ({config, name, ...}@args: {
options = {
ip = mkOption {
type = str;
description = "IP of the Redis master";
example = "192.168.1.100";
enable = mkEnableOption ''
Redis server.
Note that the NixOS module for Redis disables kernel support
for Transparent Huge Pages (THP),
because this features causes major performance problems for Redis,
e.g. (https://redis.io/topics/latency).
'';
user = mkOption {
type = types.str;
default = redisName name;
defaultText = "\"redis\" or \"redis-\${name}\" if name != \"\"";
description = "The username and groupname for redis-server.";
};
port = mkOption {
type = port;
description = "port of the Redis master";
type = types.port;
default = 6379;
description = "The port for Redis to listen to.";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};
bind = mkOption {
type = with types; nullOr str;
default = if name == "" then "127.0.0.1" else null;
defaultText = "127.0.0.1 or null if name != \"\"";
description = ''
The IP interface to bind to.
<literal>null</literal> means "all interfaces".
'';
example = "192.0.2.1";
};
unixSocket = mkOption {
type = with types; nullOr path;
default = "/run/${redisName name}/redis.sock";
defaultText = "\"/run/redis/redis.sock\" or \"/run/redis-\${name}/redis.sock\" if name != \"\"";
description = "The path to the socket to bind to.";
};
unixSocketPerm = mkOption {
type = types.int;
default = 660;
description = "Change permissions for the socket";
example = 600;
};
logLevel = mkOption {
type = types.str;
default = "notice"; # debug, verbose, notice, warning
example = "debug";
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
};
logfile = mkOption {
type = types.str;
default = "/dev/null";
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
example = "/var/log/redis.log";
};
syslog = mkOption {
type = types.bool;
default = true;
description = "Enable logging to the system logger.";
};
databases = mkOption {
type = types.int;
default = 16;
description = "Set the number of databases.";
};
maxclients = mkOption {
type = types.int;
default = 10000;
description = "Set the max number of connected clients at the same time.";
};
save = mkOption {
type = with types; listOf (listOf int);
default = [ [900 1] [300 10] [60 10000] ];
description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
};
slaveOf = mkOption {
type = with types; nullOr (submodule ({ ... }: {
options = {
ip = mkOption {
type = str;
description = "IP of the Redis master";
example = "192.168.1.100";
};
port = mkOption {
type = port;
description = "port of the Redis master";
default = 6379;
};
};
}));
default = null;
description = "IP and port to which this redis instance acts as a slave.";
example = { ip = "192.168.1.100"; port = 6379; };
};
masterAuth = mkOption {
type = with types; nullOr str;
default = null;
description = ''If the master is password protected (using the requirePass configuration)
it is possible to tell the slave to authenticate before starting the replication synchronization
process, otherwise the master will refuse the slave request.
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
};
requirePass = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
Use requirePassFile to store it outside of the nix store in a dedicated file.
'';
example = "letmein!";
};
requirePassFile = mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for the database.";
example = "/run/keys/redis-password";
};
appendOnly = mkOption {
type = types.bool;
default = false;
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
};
appendFsync = mkOption {
type = types.str;
default = "everysec"; # no, always, everysec
description = "How often to fsync the append-only log, options: no, always, everysec.";
};
slowLogLogSlowerThan = mkOption {
type = types.int;
default = 10000;
description = "Log queries whose execution take longer than X in milliseconds.";
example = 1000;
};
slowLogMaxLen = mkOption {
type = types.int;
default = 128;
description = "Maximum number of items to keep in slow log.";
};
settings = mkOption {
# TODO: this should be converted to freeformType
type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
default = {};
description = ''
Redis configuration. Refer to
<link xlink:href="https://redis.io/topics/config"/>
for details on supported values.
'';
example = literalExpression ''
{
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
}
'';
};
};
config.settings = mkMerge [
{
port = if config.bind == null then 0 else config.port;
daemonize = false;
supervised = "systemd";
loglevel = config.logLevel;
logfile = config.logfile;
syslog-enabled = config.syslog;
databases = config.databases;
maxclients = config.maxclients;
save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
dbfilename = "dump.rdb";
dir = "/var/lib/${redisName name}";
appendOnly = config.appendOnly;
appendfsync = config.appendFsync;
slowlog-log-slower-than = config.slowLogLogSlowerThan;
slowlog-max-len = config.slowLogMaxLen;
}
(mkIf (config.bind != null) { bind = config.bind; })
(mkIf (config.unixSocket != null) {
unixsocket = config.unixSocket;
unixsocketperm = toString config.unixSocketPerm;
})
(mkIf (config.slaveOf != null) { slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}"; })
(mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
(mkIf (config.requirePass != null) { requirepass = config.requirePass; })
];
}));
default = null;
description = "IP and port to which this redis instance acts as a slave.";
example = { ip = "192.168.1.100"; port = 6379; };
};
masterAuth = mkOption {
type = with types; nullOr str;
default = null;
description = ''If the master is password protected (using the requirePass configuration)
it is possible to tell the slave to authenticate before starting the replication synchronization
process, otherwise the master will refuse the slave request.
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
};
requirePass = mkOption {
type = with types; nullOr str;
default = null;
description = ''
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
Use requirePassFile to store it outside of the nix store in a dedicated file.
'';
example = "letmein!";
};
requirePassFile = mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for the database.";
example = "/run/keys/redis-password";
};
appendOnly = mkOption {
type = types.bool;
default = false;
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
};
appendFsync = mkOption {
type = types.str;
default = "everysec"; # no, always, everysec
description = "How often to fsync the append-only log, options: no, always, everysec.";
};
slowLogLogSlowerThan = mkOption {
type = types.int;
default = 10000;
description = "Log queries whose execution take longer than X in milliseconds.";
example = 1000;
};
slowLogMaxLen = mkOption {
type = types.int;
default = 128;
description = "Maximum number of items to keep in slow log.";
};
settings = mkOption {
type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
description = "Configuration of multiple <literal>redis-server</literal> instances.";
default = {};
description = ''
Redis configuration. Refer to
<link xlink:href="https://redis.io/topics/config"/>
for details on supported values.
'';
example = literalExpression ''
{
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
}
'';
};
};
@ -229,78 +289,61 @@ in {
###### implementation
config = mkIf config.services.redis.enable {
assertions = [{
assertion = cfg.requirePass != null -> cfg.requirePassFile == null;
message = "You can only set one services.redis.requirePass or services.redis.requirePassFile";
}];
boot.kernel.sysctl = (mkMerge [
config = mkIf (enabledServers != {}) {
assertions = attrValues (mapAttrs (name: conf: {
assertion = conf.requirePass != null -> conf.requirePassFile == null;
message = ''
You can only set one services.redis.servers.${name}.requirePass
or services.redis.servers.${name}.requirePassFile
'';
}) enabledServers);
boot.kernel.sysctl = mkMerge [
{ "vm.nr_hugepages" = "0"; }
( mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; } )
]);
];
networking.firewall = mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
users.users.redis = {
description = "Redis database user";
group = "redis";
isSystemUser = true;
};
users.groups.redis = {};
networking.firewall.allowedTCPPorts = concatMap (conf:
optional conf.openFirewall conf.port
) (attrValues enabledServers);
environment.systemPackages = [ cfg.package ];
services.redis.settings = mkMerge [
{
port = cfg.port;
daemonize = false;
supervised = "systemd";
loglevel = cfg.logLevel;
logfile = cfg.logfile;
syslog-enabled = cfg.syslog;
databases = cfg.databases;
maxclients = cfg.maxclients;
save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") cfg.save;
dbfilename = "dump.rdb";
dir = "/var/lib/redis";
appendOnly = cfg.appendOnly;
appendfsync = cfg.appendFsync;
slowlog-log-slower-than = cfg.slowLogLogSlowerThan;
slowlog-max-len = cfg.slowLogMaxLen;
}
(mkIf (cfg.bind != null) { bind = cfg.bind; })
(mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
(mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
(mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
(mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
];
users.users = mapAttrs' (name: conf: nameValuePair (redisName name) {
description = "System user for the redis-server instance ${name}";
isSystemUser = true;
group = redisName name;
}) enabledServers;
users.groups = mapAttrs' (name: conf: nameValuePair (redisName name) {
}) enabledServers;
systemd.services.redis = {
description = "Redis Server";
systemd.services = mapAttrs' (name: conf: nameValuePair (redisName name) {
description = "Redis Server - ${redisName name}";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
preStart = ''
install -m 600 ${redisConfig} /run/redis/redis.conf
'' + optionalString (cfg.requirePassFile != null) ''
password=$(cat ${escapeShellArg cfg.requirePassFile})
echo "requirePass $password" >> /run/redis/redis.conf
'';
serviceConfig = {
ExecStart = "${cfg.package}/bin/redis-server /run/redis/redis.conf";
ExecStart = "${cfg.package}/bin/redis-server /run/${redisName name}/redis.conf";
ExecStartPre = [("+"+pkgs.writeShellScript "${redisName name}-credentials" (''
install -o '${conf.user}' -m 600 ${redisConfig conf.settings} /run/${redisName name}/redis.conf
'' + optionalString (conf.requirePassFile != null) ''
{
printf requirePass' '
cat ${escapeShellArg conf.requirePassFile}
} >>/run/${redisName name}/redis.conf
'')
)];
Type = "notify";
# User and group
User = "redis";
Group = "redis";
User = conf.user;
Group = conf.user;
# Runtime directory and mode
RuntimeDirectory = "redis";
RuntimeDirectory = redisName name;
RuntimeDirectoryMode = "0750";
# State directory and mode
StateDirectory = "redis";
StateDirectory = redisName name;
StateDirectoryMode = "0700";
# Access write directories
UMask = "0077";
@ -309,7 +352,7 @@ in {
# Security
NoNewPrivileges = true;
# Process Properties
LimitNOFILE = "${toString ulimitNofile}";
LimitNOFILE = mkDefault "${toString (conf.maxclients + 32)}";
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
@ -322,7 +365,9 @@ in {
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictAddressFamilies =
optionals (conf.bind != null) ["AF_INET" "AF_INET6"] ++
optional (conf.unixSocket != null) "AF_UNIX";
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
@ -333,6 +378,7 @@ in {
SystemCallArchitectures = "native";
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
};
};
}) enabledServers;
};
}

View file

@ -38,7 +38,7 @@ with lib;
systemd.packages = [ pkgs.glib-networking ];
environment.sessionVariables.GIO_EXTRA_MODULES = [ "${pkgs.glib-networking.out}/lib/gio/modules" ];
environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.glib-networking.out}/lib/gio/modules" ];
};

View file

@ -57,7 +57,7 @@ in
services.udev.packages = [ pkgs.libmtp.bin ];
# Needed for unwrapped applications
environment.sessionVariables.GIO_EXTRA_MODULES = [ "${cfg.package}/lib/gio/modules" ];
environment.variables.GIO_EXTRA_MODULES = [ "${cfg.package}/lib/gio/modules" ];
};

View file

@ -40,6 +40,7 @@ in {
haskellPackages = mkOption {
description = "Which haskell package set to use.";
type = types.attrs;
default = pkgs.haskellPackages;
defaultText = literalExpression "pkgs.haskellPackages";
};

View file

@ -71,6 +71,7 @@ in {
baseq3 = mkOption {
type = types.either types.package types.path;
default = defaultBaseq3;
defaultText = literalDocBook "Manually downloaded Quake 3 installation directory.";
example = "/var/lib/q3ds";
description = ''
Path to the baseq3 files (pak*.pk3). If this is on the nix store (type = package) all .pk3 files should be saved

View file

@ -1,9 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.terraria;
opt = options.services.terraria;
worldSizeMap = { small = 1; medium = 2; large = 3; };
valFlag = name: val: optionalString (val != null) "-${name} \"${escape ["\\" "\""] (toString val)}\"";
boolFlag = name: val: optionalString val "-${name}";
@ -36,7 +37,7 @@ in
type = types.bool;
default = false;
description = ''
If enabled, starts a Terraria server. The server can be connected to via <literal>tmux -S ${cfg.dataDir}/terraria.sock attach</literal>
If enabled, starts a Terraria server. The server can be connected to via <literal>tmux -S ''${config.${opt.dataDir}}/terraria.sock attach</literal>
for administration by users who are a part of the <literal>terraria</literal> group (use <literal>C-b d</literal> shortcut to detach again).
'';
};

Some files were not shown because too many files have changed in this diff Show more