Merge commit a2a66180ba into haskell-updates

This commit is contained in:
sternenseemann
2025-03-17 12:28:00 +01:00
2464 changed files with 50937 additions and 69780 deletions
+1
View File
@@ -102,6 +102,7 @@ body:
Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
@@ -116,6 +116,7 @@ body:
If this issue is related to the Darwin packaging architecture as a whole, or is related to the core Darwin frameworks, consider mentioning the `@NixOS/darwin-core` team.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
@@ -106,6 +106,7 @@ body:
If in doubt, check `git blame` for whoever last touched the module, or check the associated package's maintainers. Please add the mentions above the `---` characters.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
@@ -109,6 +109,7 @@ body:
Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
@@ -48,6 +48,7 @@ body:
Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
@@ -121,6 +121,7 @@ body:
Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
value: |
---
**Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
+2 -2
View File
@@ -1,4 +1,4 @@
{
"rev": "5757bbb8bd7c0630a0cc4bb19c47e588db30b97c",
"sha256": "0px0lr7ad2zrws400507c9w5nnaffz9mp9hqssm64icdm6f6h0fz"
"rev": "573c650e8a14b2faa0041645ab18aed7e60f0c9a",
"sha256": "0qg99zj0gb0pc6sjlkmwhk1c1xz14qxmk6gamgfmcxpsfdp5vn72"
}
+1 -1
View File
@@ -10,6 +10,6 @@ The NixOS desktop or other non-headless configurations are the primary target fo
## Nix on GNU/Linux {#nix-on-gnulinux}
If you are using a non-NixOS GNU/Linux/X11 desktop with free software video drivers, consider launching OpenGL-dependent programs from Nixpkgs with Nixpkgs versions of `libglvnd` and `mesa.drivers` in `LD_LIBRARY_PATH`. For Mesa drivers, the Linux kernel version doesn't have to match nixpkgs.
If you are using a non-NixOS GNU/Linux/X11 desktop with free software video drivers, consider launching OpenGL-dependent programs from Nixpkgs with Nixpkgs versions of `libglvnd` and `mesa` in `LD_LIBRARY_PATH`. For Mesa drivers, the Linux kernel version doesn't have to match nixpkgs.
For proprietary video drivers, you might have luck with also adding the corresponding video driver package.
+5
View File
@@ -220,6 +220,11 @@ lib.mapAttrs mkLicense ({
fullName = "Lawrence Berkeley National Labs BSD variant license";
};
bsdAxisNoDisclaimerUnmodified = {
fullName = "BSD-Axis without Warranty Disclaimer with Unmodified requirement";
url = "https://scancode-licensedb.aboutcode.org/bsd-no-disclaimer-unmodified.html";
};
bsdOriginal = {
spdxId = "BSD-4-Clause";
fullName = ''BSD 4-clause "Original" or "Old" License'';
+98 -6
View File
@@ -2235,6 +2235,13 @@
name = "tali auster";
matrix = "@atalii:matrix.org";
};
atar13 = {
name = "Anthony Tarbinian";
email = "atar137h@gmail.com";
github = "atar13";
githubId = 42757207;
matrix = "@atar13:matrix.org";
};
ataraxiasjel = {
email = "nix@ataraxiadev.com";
github = "AtaraxiaSjel";
@@ -2451,6 +2458,12 @@
githubId = 206242;
name = "Andreas Wiese";
};
awwpotato = {
email = "awwpotato@voidq.com";
github = "awwpotato";
githubId = 153149335;
name = "awwpotato";
};
axertheaxe = {
email = "axertheaxe@proton.me";
github = "AxerTheAxe";
@@ -3188,6 +3201,12 @@
githubId = 77934086;
keys = [ { fingerprint = "4CA3 48F6 8FE1 1777 8EDA 3860 B9A2 C1B0 25EC 2C55"; } ];
};
blenderfreaky = {
name = "blenderfreaky";
email = "nix@blenderfreaky.de";
github = "blenderfreaky";
githubId = 14351657;
};
blinry = {
name = "blinry";
email = "mail@blinry.org";
@@ -4461,6 +4480,12 @@
githubId = 71959829;
name = "Cleeyv";
};
clementpoiret = {
email = "poiret.clement@outlook.fr";
github = "clementpoiret";
githubId = 10899984;
name = "Clement POIRET";
};
clemjvdm = {
email = "clement.jvdm@gmail.com";
github = "clemjvdm";
@@ -5297,7 +5322,7 @@
};
danth = {
name = "Daniel Thwaites";
email = "danthwaites30@btinternet.com";
email = "danth@danth.me";
matrix = "@danth:danth.me";
github = "danth";
githubId = 28959268;
@@ -9176,6 +9201,12 @@
githubId = 1498782;
name = "Jesse Haber-Kucharsky";
};
hakujin = {
email = "colin@hakuj.in";
github = "hakujin";
githubId = 2192042;
name = "Colin King";
};
hamburger1984 = {
email = "hamburger1984@gmail.com";
github = "hamburger1984";
@@ -9456,6 +9487,12 @@
githubId = 49935860;
name = "Henri Rosten";
};
henrispriet = {
email = "henri.spriet@gmail.com";
github = "henrispriet";
githubId = 36509362;
name = "Henri Spriet";
};
henrytill = {
email = "henrytill@gmail.com";
github = "henrytill";
@@ -9732,6 +9769,12 @@
githubId = 39689;
name = "Hugo Tavares Reis";
};
httprafa = {
email = "rafael.kienitz@gmail.com";
github = "HttpRafa";
githubId = 60099368;
name = "Rafael Kienitz";
};
huantian = {
name = "David Li";
email = "davidtianli@gmail.com";
@@ -14304,6 +14347,12 @@
}
];
};
mahyarmirrashed = {
email = "mah.mirr@gmail.com";
github = "mahyarmirrashed";
githubId = 59240843;
name = "Mahyar Mirrashed";
};
majesticmullet = {
email = "hoccthomas@gmail.com.au";
github = "MajesticMullet";
@@ -16498,6 +16547,13 @@
githubId = 56316606;
name = "Amneesh Singh";
};
naufik = {
email = "naufal@naufik.net";
github = "naufik";
githubId = 8577904;
name = "Naufal Fikri";
keys = [ { fingerprint = "1575 D651 E31EC 6117A CF0AA C1A3B 8BBC A515 8835"; } ];
};
naxdy = {
name = "Naxdy";
email = "naxdy@naxdy.org";
@@ -16506,11 +16562,6 @@
githubId = 4532582;
keys = [ { fingerprint = "BDEA AB07 909D B96F 4106 85F1 CC15 0758 46BC E91B"; } ];
};
nayeko = {
name = "nayeko";
github = "nayeko";
githubId = 196556004;
};
nazarewk = {
name = "Krzysztof Nazarewski";
email = "nixpkgs@kdn.im";
@@ -17700,6 +17751,12 @@
githubId = 34910574;
keys = [ { fingerprint = "D055 8A23 3947 B7A0 F966 B07F 0B41 0348 9833 7273"; } ];
};
Oops418 = {
email = "oooopsxxx@gmail.com";
github = "Oops418";
name = "Oops418";
githubId = 93655215;
};
oosquare = {
name = "Justin Chen";
email = "oosquare@outlook.com";
@@ -20098,6 +20155,12 @@
githubId = 10631029;
name = "Richard Ipsum";
};
richiejp = {
email = "io@richiejp.com";
github = "richiejp";
githubId = 988098;
name = "Richard Palethorpe";
};
rick68 = {
email = "rick68@gmail.com";
github = "rick68";
@@ -20483,6 +20546,12 @@
githubId = 19699320;
keys = [ { fingerprint = "FD5D F7A8 85BB 378A 0157 5356 B09C 4220 3566 9AF8"; } ];
};
RossSmyth = {
name = "Ross Smyth";
matrix = "@rosssmyth:matrix.org";
github = "RossSmyth";
githubId = 18294397;
};
rostan-t = {
name = "Rostan Tabet";
email = "rostan.tabet@gmail.com";
@@ -21387,6 +21456,12 @@
githubId = 33031;
name = "Greg Pfeil";
};
semtexerror = {
email = "github@spampert.com";
github = "SemtexError";
githubId = 8776314;
name = "Robin";
};
sengaya = {
email = "tlo@sengaya.de";
github = "sengaya";
@@ -23726,6 +23801,12 @@
githubId = 678511;
name = "Thomas Mader";
};
thornoar = {
email = "r.a.maksimovich@gmail.com";
github = "thornoar";
githubId = 84677666;
name = "Roman Maksimovich";
};
thornycrackers = {
email = "codyfh@gmail.com";
github = "thornycrackers";
@@ -23893,6 +23974,12 @@
matrix = "@titaniumtown:envs.net";
keys = [ { fingerprint = "D15E 4754 FE1A EDA1 5A6D 4702 9AB2 8AC1 0ECE 533D"; } ];
};
tjkeller = {
email = "tjk@tjkeller.xyz";
github = "tjkeller-xyz";
githubId = 36288711;
name = "Tim Keller";
};
tjni = {
email = "43ngvg@masqt.com";
matrix = "@tni:matrix.org";
@@ -26409,6 +26496,11 @@
github = "zfnmxt";
githubId = 37446532;
};
zh4ngx = {
github = "zh4ngx";
githubId = 1329212;
name = "Andy Zhang";
};
zhaofengli = {
email = "hello@zhaofeng.li";
matrix = "@zhaofeng:zhaofeng.li";
+5 -1
View File
@@ -444,6 +444,7 @@ with lib.maintainers;
members = [
globin
krav
leona
talyz
yayayayaka
];
@@ -548,7 +549,10 @@ with lib.maintainers;
};
infisical = {
members = [ akhilmhdh ];
members = [
akhilmhdh
mahyarmirrashed
];
scope = "Maintain Infisical";
shortName = "Infisical";
};
@@ -21,6 +21,11 @@
- LLVM has been updated from LLVM 16 (on Darwin) and LLVM 18 (on other platforms) to LLVM 19.
This introduces some backwardsincompatible changes; see the [upstream release notes](https://releases.llvm.org/) for details.
- Emacs has been updated to 30.1.
This introduces some backwardsincompatible changes; see the NEWS for details.
NEWS can been viewed from Emacs by typing `C-h n`, or by clicking `Help->Emacs News` from the menu bar.
It can also be browsed [online](https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS?h=emacs-30).
- The default PHP version has been updated to 8.3.
- The default Erlang OTP version has been updated to 27.
@@ -176,6 +181,8 @@
- [echoip](https://github.com/mpolden/echoip), a simple service for looking up your IP address. Available as [services.echoip](#opt-services.echoip.enable).
- [LiteLLM](https://github.com/BerriAI/litellm), a LLM Gateway to provide model access, fallbacks and spend tracking across 100+ LLMs. All in the OpenAI format. Available as [services.litellm](#opt-services.litellm.enable).
- [Buffyboard](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/tree/master/buffyboard), a framebuffer on-screen keyboard. Available as [services.buffyboard](option.html#opt-services.buffyboard).
- [KanBoard](https://github.com/kanboard/kanboard), a project management tool that focuses on the Kanban methodology. Available as [services.kanboard](#opt-services.kanboard.enable).
@@ -190,6 +197,8 @@
- [Limine](https://github.com/limine-bootloader/limine) a modern, advanced, portable, multiprotocol bootloader and boot manager. Available as [boot.loader.limine](#opt-boot.loader.limine.enable)
- [Orthanc](https://orthanc.uclouvain.be/) a lightweight, RESTful DICOM server for healthcare and medical research. Available as [services.orthanc](#opt-services.orthanc.enable).
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
## Backward Incompatibilities {#sec-release-25.05-incompatibilities}
@@ -237,6 +246,8 @@
- `pytestFlagsArray` and `unittestFlagsArray` are kept for compatibility purposes. They continue to be Bash-expanded before concatenated. This compatibility layer will be removed in future releases.
- The `haka` package and module has been removed because the package was broken and unmaintained for 9 years.
- `strawberry` has been updated to 1.2, which drops support for the VLC backend and Qt 5. The `strawberry-qt5` package
and `withGstreamer`/`withVlc` override options have been removed due to this.
@@ -264,6 +275,8 @@
- `isd` was updated from 0.2.0 to 0.5.1, the new version may crash with a previously generated config, try moving or deleting `~/.config/isd/schema.json`.
- `uwsgi` no longer supports Python 2 plugins.
- `asusd` has been upgraded to version 6 which supports multiple aura devices. To account for this, the single `auraConfig` configuration option has been replaced with `auraConfigs` which is an attribute set of config options per each device. The config files may also be now specified as either source files or text strings; to account for this you will need to specify that `text` is used for your existing configs, e.g.:
```diff
-services.asusd.asusdConfig = '''file contents'''
@@ -443,6 +456,8 @@
- `docker_24` has been removed, as it was EOL with vulnerabilities since June 08, 2024.
- Emacs 28 and 29 have been removed.
- `containerd` has been updated to v2, which contains breaking changes. See the [containerd
2.0](https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md) documentation for more
details.
@@ -481,6 +496,8 @@
- `security.apparmor.policies.<name>.enforce` and `security.apparmor.policies.<name>.enable` were removed.
Configuring the state of apparmor policies must now be done using `security.apparmor.policies.<name>.state` tristate option.
- `services.graylog.package` now defaults to `graylog-6_0` as previous default `graylog-5_1` is EOL and therefore removed.
Check the migration guides on [5.1→5.2](https://go2docs.graylog.org/5-2/upgrading_graylog/upgrading_to_graylog_5.2.x.htm) and [5.2→6.0](https://go2docs.graylog.org/6-0/upgrading_graylog/upgrading_to_graylog_6.0.x.html) for breaking changes.
- the notmuch vim plugin now lives in a separate output of the `notmuch`
package. Installing `notmuch` will not bring the notmuch vim package anymore,
@@ -503,6 +520,8 @@
- `programs.clash-verge.tunMode` was deprecated and removed because now service mode is necessary to start program. Without `programs.clash-verge.enable`, clash-verge-rev will refuse to start.
- `confluent-cli` was updated from 3.60.0 to 4.16.0, which includes several breaking changes as detailed in [Confluent's release notes](https://docs.confluent.io/confluent-cli/current/release-notes.html).
- `siduck76-st` has been renamed to `st-snazzy`, like the project's [flake](https://github.com/siduck/st/blob/main/flake.nix).
- `python3Packages.jax` now directly depends on `python3Packages.jaxlib`.
@@ -573,6 +592,8 @@
- `services.kmonad` now creates a determinate symlink (in `/dev/input/by-id/`) to each of KMonad virtual devices.
- `services.searx` now supports configuration of the favicons cache and other options available in SearXNG's `favicons.toml` file
- `services.gitea` now supports CAPTCHA usage through the `services.gitea.captcha` variable.
- `services.soft-serve` now restarts upon config change.
@@ -587,6 +608,8 @@
- New options for the declarative configuration of the user space part of ALSA have been introduced under [hardware.alsa](options.html#opt-hardware.alsa.enable), including setting the default capture and playback device, defining sound card aliases and volume controls.
Note: these are intended for users not running a sound server like PulseAudio or PipeWire, but having ALSA as their only sound system.
- `services.k3s` now provides the `autoDeployCharts` option that allows to automatically deploy Helm charts via the k3s Helm controller.
- Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added.
Example:
+2 -2
View File
@@ -120,7 +120,7 @@ in
{ "r" = {}; };
};
hardware.graphics.package = lib.mkDefault pkgs.mesa.drivers;
hardware.graphics.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa.drivers;
hardware.graphics.package = lib.mkDefault pkgs.mesa;
hardware.graphics.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa;
};
}
@@ -75,6 +75,7 @@ in
config = mkIf cfg.enable {
systemd.services.hddtemp = {
description = "HDD/SSD temperature";
documentation = [ "man:hddtemp(8)" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "forking";
+3
View File
@@ -24,6 +24,7 @@
# compression tools
, zstd
, xz
, zeekstd
# arguments
, name
@@ -89,11 +90,13 @@ let
compressionPkg = {
"zstd" = zstd;
"xz" = xz;
"zstd-seekable" = zeekstd;
}."${compression.algorithm}";
compressionCommand = {
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
"zstd-seekable" = "zeekstd --quiet --max-frame-size 2M --compression-level ${toString compression.level}";
}."${compression.algorithm}";
in
stdenvNoCC.mkDerivation (finalAttrs:
+5 -3
View File
@@ -48,7 +48,7 @@ let
};
repartConfig = lib.mkOption {
type = with lib.types; attrsOf (oneOf [ str int bool ]);
type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
example = {
Type = "home";
SizeMinBytes = "512M";
@@ -113,7 +113,7 @@ in
enable = lib.mkEnableOption "Image compression";
algorithm = lib.mkOption {
type = lib.types.enum [ "zstd" "xz" ];
type = lib.types.enum [ "zstd" "xz" "zstd-seekable" ];
default = "zstd";
description = "Compression algorithm";
};
@@ -274,6 +274,7 @@ in
{
"zstd" = ".zst";
"xz" = ".xz";
"zstd-seekable" = ".zst";
}."${cfg.compression.algorithm}";
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
@@ -298,6 +299,7 @@ in
level = lib.mkOptionDefault {
"zstd" = 3;
"xz" = 3;
"zstd-seekable" = 3;
}."${cfg.compression.algorithm}";
};
@@ -311,7 +313,7 @@ in
(lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
format = pkgs.formats.ini { };
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
definitionsDirectory = utils.systemdUtils.lib.definitions
"repart.d"
+2 -1
View File
@@ -825,6 +825,7 @@
./services/misc/languagetool.nix
./services/misc/leaps.nix
./services/misc/lifecycled.nix
./services/misc/litellm.nix
./services/misc/llama-cpp.nix
./services/misc/logkeys.nix
./services/misc/mame.nix
@@ -848,6 +849,7 @@
./services/misc/ombi.nix
./services/misc/omnom.nix
./services/misc/open-webui.nix
./services/misc/orthanc.nix
./services/misc/osrm.nix
./services/misc/owncast.nix
./services/misc/packagekit.nix
@@ -1385,7 +1387,6 @@
./services/security/esdm.nix
./services/security/fail2ban.nix
./services/security/fprintd.nix
./services/security/haka.nix
./services/security/haveged.nix
./services/security/hockeypuck.nix
./services/security/hologram-agent.nix
@@ -127,8 +127,6 @@ in
system.disableInstallerTools = true;
nix.settings = {
auto-optimise-store = true;
min-free = cfg.min-free;
max-free = cfg.max-free;
+9 -1
View File
@@ -13,11 +13,19 @@ in
programs.bash.enableLsColors = lib.mkEnableOption "extra colors in directory listings" // {
default = true;
};
programs.bash.lsColorsFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
example = lib.literalExpression "\${pkgs.dircolors-solarized}/ansi-dark";
description = "Alternative colorscheme for ls colors";
};
};
config = lib.mkIf enable {
programs.bash.promptPluginInit = ''
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
eval "$(${pkgs.coreutils}/bin/dircolors -b ${
lib.optionalString (config.programs.bash.lsColorsFile != null) config.programs.bash.lsColorsFile
})"
'';
};
}
+1 -1
View File
@@ -59,7 +59,7 @@ let
shell:
if (shell != "fish") then
''
eval $(${getExe finalPackage} ${shell} --alias ${cfg.alias})
eval "$(${getExe finalPackage} ${shell} --alias ${cfg.alias})"
''
else
''
+3
View File
@@ -292,6 +292,9 @@ in
See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
Please switch to a different implementation like kea or dnsmasq.
'')
(mkRemovedOptionModule [ "services" "haka" ] ''
The corresponding package was broken and removed from nixpkgs.
'')
(mkRemovedOptionModule [ "services" "tedicross" ] ''
The corresponding package was broken and removed from nixpkgs.
'')
+1
View File
@@ -15,6 +15,7 @@
systemd.services.auditd = {
description = "Linux Audit daemon";
documentation = [ "man:auditd(8)" ];
wantedBy = [ "sysinit.target" ];
after = [
"local-fs.target"
+1
View File
@@ -128,6 +128,7 @@ in
systemd.services.isolate = {
description = "Isolate control group hierarchy daemon";
wantedBy = [ "multi-user.target" ];
documentation = [ "man:isolate(1)" ];
serviceConfig = {
Type = "notify";
ExecStart = "${isolate}/bin/isolate-cg-keeper";
+514 -169
View File
@@ -20,110 +20,355 @@ let
chartDir = "/var/lib/rancher/k3s/server/static/charts";
imageDir = "/var/lib/rancher/k3s/agent/images";
containerdConfigTemplateFile = "/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl";
yamlFormat = pkgs.formats.yaml { };
yamlDocSeparator = builtins.toFile "yaml-doc-separator" "\n---\n";
# Manifests need a valid YAML suffix to be respected by k3s
mkManifestTarget =
name: if (lib.hasSuffix ".yaml" name || lib.hasSuffix ".yml" name) then name else name + ".yaml";
# Produces a list containing all duplicate manifest names
duplicateManifests =
with builtins;
lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.manifests);
# Produces a list containing all duplicate chart names
duplicateCharts =
with builtins;
lib.intersectLists (attrNames cfg.autoDeployCharts) (attrNames cfg.charts);
manifestModule =
let
mkTarget =
name: if (lib.hasSuffix ".yaml" name || lib.hasSuffix ".yml" name) then name else name + ".yaml";
in
lib.types.submodule (
{
name,
config,
options,
...
}:
{
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether this manifest file should be generated.";
};
target = lib.mkOption {
type = lib.types.nonEmptyStr;
example = lib.literalExpression "manifest.yaml";
description = ''
Name of the symlink (relative to {file}`${manifestDir}`).
Defaults to the attribute name.
'';
};
content = lib.mkOption {
type = with lib.types; nullOr (either attrs (listOf attrs));
default = null;
description = ''
Content of the manifest file. A single attribute set will
generate a single document YAML file. A list of attribute sets
will generate multiple documents separated by `---` in a single
YAML file.
'';
};
source = lib.mkOption {
type = lib.types.path;
example = lib.literalExpression "./manifests/app.yaml";
description = ''
Path of the source `.yaml` file.
'';
};
};
config = {
target = lib.mkDefault (mkTarget name);
source = lib.mkIf (config.content != null) (
let
name' = "k3s-manifest-" + builtins.baseNameOf name;
docName = "k3s-manifest-doc-" + builtins.baseNameOf name;
yamlDocSeparator = builtins.toFile "yaml-doc-separator" "\n---\n";
mkYaml = name: x: (pkgs.formats.yaml { }).generate name x;
mkSource =
value:
if builtins.isList value then
pkgs.concatText name' (
lib.concatMap (x: [
yamlDocSeparator
(mkYaml docName x)
]) value
)
else
mkYaml name' value;
in
lib.mkDerivedConfig options.content mkSource
);
};
}
# Converts YAML -> JSON -> Nix
fromYaml =
path:
with builtins;
fromJSON (
readFile (
pkgs.runCommand "${path}-converted.json" { nativeBuildInputs = [ yq-go ]; } ''
yq --no-colors --output-format json ${path} > $out
''
)
);
enabledManifests = lib.filter (m: m.enable) (lib.attrValues cfg.manifests);
linkManifestEntry = m: "${pkgs.coreutils-full}/bin/ln -sfn ${m.source} ${manifestDir}/${m.target}";
linkImageEntry = image: "${pkgs.coreutils-full}/bin/ln -sfn ${image} ${imageDir}/${image.name}";
linkChartEntry =
let
mkTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
in
# Replace characters that are problematic in file names
cleanHelmChartName =
lib.replaceStrings
[
"/"
":"
]
[
"-"
"-"
];
# Fetch a Helm chart from a public registry. This only supports a basic Helm pull.
fetchHelm =
{
name,
repo,
version,
hash ? lib.fakeHash,
}:
pkgs.runCommand (cleanHelmChartName "${lib.removePrefix "https://" repo}-${name}-${version}.tgz")
{
inherit (lib.fetchers.normalizeHash { } { inherit hash; }) outputHash outputHashAlgo;
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
nativeBuildInputs = with pkgs; [
kubernetes-helm
cacert
];
}
''
export HOME="$PWD"
helm repo add repository ${repo}
helm pull repository/${name} --version ${version}
mv ./*.tgz $out
'';
# Returns the path to a YAML manifest file
mkExtraDeployManifest =
x:
# x is a derivation that provides a YAML file
if lib.isDerivation x then
x.outPath
# x is an attribute set that needs to be converted to a YAML file
else if builtins.isAttrs x then
(yamlFormat.generate "extra-deploy-chart-manifest" x)
# assume x is a path to a YAML file
else
x;
# Generate a HelmChart custom resource.
mkHelmChartCR =
name: value:
"${pkgs.coreutils-full}/bin/ln -sfn ${value} ${chartDir}/${mkTarget (builtins.baseNameOf name)}";
let
chartValues = if (lib.isPath value.values) then fromYaml value.values else value.values;
# use JSON for values as it's a subset of YAML and understood by the k3s Helm controller
valuesContent = builtins.toJSON chartValues;
in
# merge with extraFieldDefinitions to allow setting advanced values and overwrite generated
# values
lib.recursiveUpdate {
apiVersion = "helm.cattle.io/v1";
kind = "HelmChart";
metadata = {
inherit name;
namespace = "kube-system";
};
spec = {
inherit valuesContent;
inherit (value) targetNamespace createNamespace;
chart = "https://%{KUBERNETES_API}%/static/charts/${name}.tgz";
};
} value.extraFieldDefinitions;
activateK3sContent = pkgs.writeShellScript "activate-k3s-content" ''
${lib.optionalString (
builtins.length enabledManifests > 0
) "${pkgs.coreutils-full}/bin/mkdir -p ${manifestDir}"}
${lib.optionalString (cfg.charts != { }) "${pkgs.coreutils-full}/bin/mkdir -p ${chartDir}"}
${lib.optionalString (
builtins.length cfg.images > 0
) "${pkgs.coreutils-full}/bin/mkdir -p ${imageDir}"}
# Generate a HelmChart custom resource together with extraDeploy manifests. This
# generates possibly a multi document YAML file that the auto deploy mechanism of k3s
# deploys.
mkAutoDeployChartManifest = name: value: {
# target is the final name of the link created for the manifest file
target = mkManifestTarget name;
inherit (value) enable package;
# source is a store path containing the complete manifest file
source = pkgs.concatText "auto-deploy-chart-${name}.yaml" (
[
(yamlFormat.generate "helm-chart-manifest-${name}.yaml" (mkHelmChartCR name value))
]
# alternate the YAML doc seperator (---) and extraDeploy manifests to create
# multi document YAMLs
++ (lib.concatMap (x: [
yamlDocSeparator
(mkExtraDeployManifest x)
]) value.extraDeploy)
);
};
${builtins.concatStringsSep "\n" (map linkManifestEntry enabledManifests)}
${builtins.concatStringsSep "\n" (lib.mapAttrsToList linkChartEntry cfg.charts)}
${builtins.concatStringsSep "\n" (map linkImageEntry cfg.images)}
autoDeployChartsModule = lib.types.submodule (
{ config, ... }:
{
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
example = false;
description = ''
Whether to enable the installation of this Helm chart. Note that setting
this option to `false` will not uninstall the chart from the cluster, if
it was previously installed. Please use the the `--disable` flag or `.skip`
files to delete/disable Helm charts, as mentioned in the
[docs](https://docs.k3s.io/installation/packaged-components#disabling-manifests).
'';
};
${lib.optionalString (cfg.containerdConfigTemplate != null) ''
mkdir -p $(dirname ${containerdConfigTemplateFile})
${pkgs.coreutils-full}/bin/ln -sfn ${pkgs.writeText "config.toml.tmpl" cfg.containerdConfigTemplate} ${containerdConfigTemplateFile}
''}
'';
repo = lib.mkOption {
type = lib.types.nonEmptyStr;
example = "https://kubernetes.github.io/ingress-nginx";
description = ''
The repo of the Helm chart. Only has an effect if `package` is not set.
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
filesystem.
'';
};
name = lib.mkOption {
type = lib.types.nonEmptyStr;
example = "ingress-nginx";
description = ''
The name of the Helm chart. Only has an effect if `package` is not set.
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
filesystem.
'';
};
version = lib.mkOption {
type = lib.types.nonEmptyStr;
example = "4.7.0";
description = ''
The version of the Helm chart. Only has an effect if `package` is not set.
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
filesystem.
'';
};
hash = lib.mkOption {
type = lib.types.str;
example = "sha256-ej+vpPNdiOoXsaj1jyRpWLisJgWo8EqX+Z5VbpSjsPA=";
description = ''
The hash of the packaged Helm chart. Only has an effect if `package` is not set.
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
filesystem.
'';
};
package = lib.mkOption {
type = with lib.types; either path package;
example = lib.literalExpression "../my-helm-chart.tgz";
description = ''
The packaged Helm chart. Overwrites the options `repo`, `name`, `version`
and `hash` in case of conflicts.
'';
};
targetNamespace = lib.mkOption {
type = lib.types.nonEmptyStr;
default = "default";
example = "kube-system";
description = "The namespace in which the Helm chart gets installed.";
};
createNamespace = lib.mkOption {
type = lib.types.bool;
default = false;
example = true;
description = "Whether to create the target namespace if not present.";
};
values = lib.mkOption {
type = with lib.types; either path attrs;
default = { };
example = {
replicaCount = 3;
hostName = "my-host";
server = {
name = "nginx";
port = 80;
};
};
description = ''
Override default chart values via Nix expressions. This is equivalent to setting
values in a `values.yaml` file.
WARNING: The values (including secrets!) specified here are exposed unencrypted
in the world-readable nix store.
'';
};
extraDeploy = lib.mkOption {
type = with lib.types; listOf (either path attrs);
default = [ ];
example = lib.literalExpression ''
[
../manifests/my-extra-deployment.yaml
{
apiVersion = "v1";
kind = "Service";
metadata = {
name = "app-service";
};
spec = {
selector = {
"app.kubernetes.io/name" = "MyApp";
};
ports = [
{
name = "name-of-service-port";
protocol = "TCP";
port = 80;
targetPort = "http-web-svc";
}
];
};
}
];
'';
description = "List of extra Kubernetes manifests to deploy with this Helm chart.";
};
extraFieldDefinitions = lib.mkOption {
inherit (yamlFormat) type;
default = { };
example = {
spec = {
bootstrap = true;
helmVersion = "v2";
backOffLimit = 3;
jobImage = "custom-helm-controller:v0.0.1";
};
};
description = ''
Extra HelmChart field definitions that are merged with the rest of the HelmChart
custom resource. This can be used to set advanced fields or to overwrite
generated fields. See https://docs.k3s.io/helm#helmchart-field-definitions
for possible fields.
'';
};
};
config.package = lib.mkDefault (fetchHelm {
inherit (config)
repo
name
version
hash
;
});
}
);
manifestModule = lib.types.submodule (
{
name,
config,
options,
...
}:
{
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether this manifest file should be generated.";
};
target = lib.mkOption {
type = lib.types.nonEmptyStr;
example = "manifest.yaml";
description = ''
Name of the symlink (relative to {file}`${manifestDir}`).
Defaults to the attribute name.
'';
};
content = lib.mkOption {
type = with lib.types; nullOr (either attrs (listOf attrs));
default = null;
description = ''
Content of the manifest file. A single attribute set will
generate a single document YAML file. A list of attribute sets
will generate multiple documents separated by `---` in a single
YAML file.
'';
};
source = lib.mkOption {
type = lib.types.path;
example = lib.literalExpression "./manifests/app.yaml";
description = ''
Path of the source `.yaml` file.
'';
};
};
config = {
target = lib.mkDefault (mkManifestTarget name);
source = lib.mkIf (config.content != null) (
let
name' = "k3s-manifest-" + builtins.baseNameOf name;
docName = "k3s-manifest-doc-" + builtins.baseNameOf name;
mkSource =
value:
if builtins.isList value then
pkgs.concatText name' (
lib.concatMap (x: [
yamlDocSeparator
(yamlFormat.generate docName x)
]) value
)
else
yamlFormat.generate name' value;
in
lib.mkDerivedConfig options.content mkSource
);
};
}
);
in
{
imports = [ (removeOption [ "docker" ] "k3s docker option is no longer supported.") ];
@@ -242,78 +487,80 @@ in
type = lib.types.attrsOf manifestModule;
default = { };
example = lib.literalExpression ''
deployment.source = ../manifests/deployment.yaml;
my-service = {
enable = false;
target = "app-service.yaml";
content = {
apiVersion = "v1";
kind = "Service";
metadata = {
name = "app-service";
};
spec = {
selector = {
"app.kubernetes.io/name" = "MyApp";
{
deployment.source = ../manifests/deployment.yaml;
my-service = {
enable = false;
target = "app-service.yaml";
content = {
apiVersion = "v1";
kind = "Service";
metadata = {
name = "app-service";
};
spec = {
selector = {
"app.kubernetes.io/name" = "MyApp";
};
ports = [
{
name = "name-of-service-port";
protocol = "TCP";
port = 80;
targetPort = "http-web-svc";
}
];
};
ports = [
{
name = "name-of-service-port";
protocol = "TCP";
port = 80;
targetPort = "http-web-svc";
}
];
};
}
};
};
nginx.content = [
{
apiVersion = "v1";
kind = "Pod";
metadata = {
name = "nginx";
labels = {
"app.kubernetes.io/name" = "MyApp";
nginx.content = [
{
apiVersion = "v1";
kind = "Pod";
metadata = {
name = "nginx";
labels = {
"app.kubernetes.io/name" = "MyApp";
};
};
};
spec = {
containers = [
{
name = "nginx";
image = "nginx:1.14.2";
ports = [
{
containerPort = 80;
name = "http-web-svc";
}
];
}
];
};
}
{
apiVersion = "v1";
kind = "Service";
metadata = {
name = "nginx-service";
};
spec = {
selector = {
"app.kubernetes.io/name" = "MyApp";
spec = {
containers = [
{
name = "nginx";
image = "nginx:1.14.2";
ports = [
{
containerPort = 80;
name = "http-web-svc";
}
];
}
];
};
ports = [
{
name = "name-of-service-port";
protocol = "TCP";
port = 80;
targetPort = "http-web-svc";
}
];
};
}
];
}
{
apiVersion = "v1";
kind = "Service";
metadata = {
name = "nginx-service";
};
spec = {
selector = {
"app.kubernetes.io/name" = "MyApp";
};
ports = [
{
name = "name-of-service-port";
protocol = "TCP";
port = 80;
targetPort = "http-web-svc";
}
];
};
}
];
};
'';
description = ''
Auto-deploying manifests that are linked to {file}`${manifestDir}` before k3s starts.
@@ -337,10 +584,9 @@ in
Packaged Helm charts that are linked to {file}`${chartDir}` before k3s starts.
The attribute name will be used as the link target (relative to {file}`${chartDir}`).
The specified charts will only be placed on the file system and made available to the
Kubernetes APIServer from within the cluster, you may use the
[k3s Helm controller](https://docs.k3s.io/helm#using-the-helm-controller)
to deploy the charts. This option only makes sense on server nodes
(`role = server`).
Kubernetes APIServer from within the cluster. See the [](#opt-services.k3s.autoDeployCharts)
option and the [k3s Helm controller docs](https://docs.k3s.io/helm#using-the-helm-controller)
to deploy Helm charts. This option only makes sense on server nodes (`role = server`).
'';
};
@@ -450,6 +696,53 @@ in
set the `clientConnection.kubeconfig` if you want to use `extraKubeProxyConfig`.
'';
};
autoDeployCharts = lib.mkOption {
type = lib.types.attrsOf autoDeployChartsModule;
apply = lib.mapAttrs mkAutoDeployChartManifest;
default = { };
example = lib.literalExpression ''
{
harbor = {
name = "harbor";
repo = "https://helm.goharbor.io";
version = "1.14.0";
hash = "sha256-fMP7q1MIbvzPGS9My91vbQ1d3OJMjwc+o8YE/BXZaYU=";
values = {
existingSecretAdminPassword = "harbor-admin";
expose = {
tls = {
enabled = true;
certSource = "secret";
secret.secretName = "my-tls-secret";
};
ingress = {
hosts.core = "example.com";
className = "nginx";
};
};
};
};
custom-chart = {
package = ../charts/my-chart.tgz;
values = ../values/my-values.yaml;
extraFieldDefinitions = {
spec.timeout = "60s";
};
};
}
'';
description = ''
Auto deploying Helm charts that are installed by the k3s Helm controller. Avoid to use
attribute names that are also used in the [](#opt-services.k3s.manifests) and
[](#opt-services.k3s.charts) options. Manifests with the same name will override
auto deploying charts with the same name. Similiarly, charts with the same name will
overwrite the Helm chart contained in auto deploying charts. This option only makes
sense on server nodes (`role = server`). See the
[k3s Helm documentation](https://docs.k3s.io/helm) for further information.
'';
};
};
# implementation
@@ -462,6 +755,15 @@ in
++ (lib.optional (cfg.role != "server" && cfg.charts != { })
"k3s: Helm charts are only made available to the cluster on server nodes (role == server), they will be ignored by this node."
)
++ (lib.optional (cfg.role != "server" && cfg.autoDeployCharts != { })
"k3s: Auto deploying Helm charts are only installed on server nodes (role == server), they will be ignored by this node."
)
++ (lib.optional (duplicateManifests != [ ])
"k3s: The following auto deploying charts are overriden by manifests of the same name: ${toString duplicateManifests}."
)
++ (lib.optional (duplicateCharts != [ ])
"k3s: The following auto deploying charts are overriden by charts of the same name: ${toString duplicateCharts}."
)
++ (lib.optional (
cfg.disableAgent && cfg.images != [ ]
) "k3s: Images are only imported on nodes with an enabled agent, they will be ignored by this node")
@@ -486,6 +788,50 @@ in
environment.systemPackages = [ config.services.k3s.package ];
# Use systemd-tmpfiles to activate k3s content
systemd.tmpfiles.settings."10-k3s" =
let
# Merge manifest with manifests generated from auto deploying charts, keep only enabled manifests
enabledManifests = lib.filterAttrs (_: v: v.enable) (cfg.autoDeployCharts // cfg.manifests);
# Merge charts with charts contained in enabled auto deploying charts
helmCharts =
(lib.concatMapAttrs (n: v: { ${n} = v.package; }) (
lib.filterAttrs (_: v: v.enable) cfg.autoDeployCharts
))
// cfg.charts;
# Make a systemd-tmpfiles rule for a manifest
mkManifestRule = manifest: {
name = "${manifestDir}/${manifest.target}";
value = {
"L+".argument = "${manifest.source}";
};
};
# Ensure that all chart targets have a .tgz suffix
mkChartTarget = name: if (lib.hasSuffix ".tgz" name) then name else name + ".tgz";
# Make a systemd-tmpfiles rule for a chart
mkChartRule = target: source: {
name = "${chartDir}/${mkChartTarget target}";
value = {
"L+".argument = "${source}";
};
};
# Make a systemd-tmpfiles rule for a container image
mkImageRule = image: {
name = "${imageDir}/${image.name}";
value = {
"L+".argument = "${image}";
};
};
in
(lib.mapAttrs' (_: v: mkManifestRule v) enabledManifests)
// (lib.mapAttrs' (n: v: mkChartRule n v) helmCharts)
// (builtins.listToAttrs (map mkImageRule cfg.images))
// (lib.optionalAttrs (cfg.containerdConfigTemplate != null) {
${containerdConfigTemplateFile} = {
"L+".argument = "${pkgs.writeText "config.toml.tmpl" cfg.containerdConfigTemplate}";
};
});
systemd.services.k3s =
let
kubeletParams =
@@ -533,7 +879,6 @@ in
LimitCORE = "infinity";
TasksMax = "infinity";
EnvironmentFile = cfg.environmentFile;
ExecStartPre = activateK3sContent;
ExecStart = lib.concatStringsSep " \\\n " (
[ "${cfg.package}/bin/k3s ${cfg.role}" ]
++ (lib.optional cfg.clusterInit "--cluster-init")
@@ -70,6 +70,7 @@ in
};
systemd.services.torque-server = {
documentation = [ "man:pbs_server(8)" ];
path = [ torque ];
wantedBy = [ "multi-user.target" ];
@@ -93,6 +94,7 @@ in
};
systemd.services.torque-scheduler = {
documentation = [ "man:pbs_sched(8)" ];
path = [ torque ];
requires = [ "torque-server-init.service" ];
@@ -93,11 +93,7 @@ let
};
} cfg.extraConfig;
configFile = pkgs.runCommandLocal "config.toml" { } ''
${pkgs.buildPackages.remarshal}/bin/remarshal -if json -of toml \
< ${pkgs.writeText "config.json" (builtins.toJSON configOptions)} \
> $out
'';
configFile = (pkgs.formats.toml {}).generate "config.toml" configOptions;
in
{
@@ -372,10 +372,13 @@ in
) "${lv2Plugins}/lib/lv2";
# Mask pw-pulse if it's not wanted
systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable;
systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable;
systemd.services.pipewire-pulse.enable = cfg.pulse.enable && cfg.systemWide;
systemd.sockets.pipewire-pulse.enable = cfg.pulse.enable && cfg.systemWide;
systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable && !cfg.systemWide;
systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable && !cfg.systemWide;
systemd.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
systemd.sockets.pipewire-pulse.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
systemd.user.sockets.pipewire-pulse.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
@@ -140,12 +140,10 @@ let
}
);
configFile = pkgs.runCommandLocal "config.toml" { } ''
${pkgs.buildPackages.jq}/bin/jq 'del(..|nulls)' \
< ${pkgs.writeText "config.json" (builtins.toJSON athensConfig)} | \
${pkgs.buildPackages.remarshal}/bin/remarshal -if json -of toml \
> $out
'';
configFile = lib.pipe athensConfig [
(lib.filterAttrsRecursive (_k: v: v != null))
((pkgs.formats.toml {}).generate "config.toml")
];
in
{
meta = {
@@ -127,5 +127,7 @@
services.libeufin.nexus.settings.libeufin-nexusdb-postgres.CONFIG = lib.mkIf (
cfgMain.bank.enable && cfgMain.bank.createLocalDatabase
) "postgresql:///libeufin-bank";
systemd.services.libeufin-nexus.documentation = [ "man:libeufin-nexus(1)" ];
};
}
@@ -68,11 +68,19 @@ in
requires = [ "taler-${talerComponent}-dbinit.service" ];
after = [ "taler-${talerComponent}-dbinit.service" ];
wantedBy = [ "multi-user.target" ]; # TODO slice?
documentation = [
"man:taler-${talerComponent}-${name}(1)"
"info:taler-${talerComponent}"
];
}))
# Database Initialisation
{
"taler-${talerComponent}-dbinit" = {
path = [ config.services.postgresql.package ];
documentation = [
"man:taler-${talerComponent}-dbinit(1)"
"info:taler-${talerComponent}"
];
serviceConfig = {
Type = "oneshot";
DynamicUser = true;
@@ -148,7 +148,7 @@ in {
};
package = lib.mkPackageOption pkgs "minecraft-server" {
example = "minecraft-server_1_12_2";
example = "pkgs.minecraft-server_1_12_2";
};
jvmOpts = lib.mkOption {
@@ -49,6 +49,7 @@ in
systemd.services.thermald = {
description = "Thermal Daemon Service";
documentation = [ "man:thermald(8)" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
PrivateNetwork = true;
@@ -122,6 +122,7 @@ in
systemd.services.triggerhappy = {
wantedBy = [ "multi-user.target" ];
description = "Global hotkey daemon";
documentation = [ "man:thd(1)" ];
serviceConfig = {
ExecStart = "${pkgs.triggerhappy}/bin/thd ${
lib.optionalString (cfg.user != "root") "--user ${cfg.user}"
+18 -11
View File
@@ -38,17 +38,8 @@ in
enable = lib.mkEnableOption "Graylog, a log management solution";
package = lib.mkOption {
type = lib.types.package;
default =
if lib.versionOlder config.system.stateVersion "23.05" then pkgs.graylog-3_3 else pkgs.graylog-5_1;
defaultText = lib.literalExpression (
if lib.versionOlder config.system.stateVersion "23.05" then
"pkgs.graylog-3_3"
else
"pkgs.graylog-5_1"
);
description = "Graylog package to use.";
package = lib.mkPackageOption pkgs "graylog" {
example = "graylog-6_0";
};
user = lib.mkOption {
@@ -139,6 +130,22 @@ in
config = lib.mkIf cfg.enable {
# Note: when changing the default, make it conditional on
# system.stateVersion to maintain compatibility with existing
# systems!
services.graylog.package =
let
mkThrow = ver: throw "graylog-${ver} was removed, please upgrade your graylog version.";
base =
if lib.versionAtLeast config.system.stateVersion "25.05" then
pkgs.graylog-6_0
else if lib.versionAtLeast config.system.stateVersion "23.05" then
mkThrow "5_1"
else
mkThrow "3_3";
in
lib.mkDefault base;
users.users = lib.mkIf (cfg.user == "graylog") {
graylog = {
isSystemUser = true;
+6 -4
View File
@@ -2,9 +2,11 @@
let
cfg = config.services.promtail;
prettyJSON = conf: pkgs.runCommandLocal "promtail-config.json" {} ''
echo '${builtins.toJSON conf}' | ${pkgs.buildPackages.jq}/bin/jq 'del(._module)' > $out
'';
format = pkgs.formats.json {};
prettyJSON = conf: with lib; pipe conf [
(flip removeAttrs [ "_module" ])
(format.generate "promtail-config.json")
];
allowSystemdJournal = cfg.configuration ? scrape_configs && lib.any (v: v ? journal) cfg.configuration.scrape_configs;
@@ -20,7 +22,7 @@ in {
enable = mkEnableOption "the Promtail ingresser";
configuration = mkOption {
type = (pkgs.formats.json {}).type;
type = format.type;
description = ''
Specify the configuration for Promtail in Nix.
This option will be ignored if `services.promtail.configFile` is defined.
+1
View File
@@ -871,6 +871,7 @@ in
systemd.services.postfix = {
description = "Postfix mail server";
documentation = [ "man:postfix(1)" ];
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
+182
View File
@@ -0,0 +1,182 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) types;
cfg = config.services.litellm;
settingsFormat = pkgs.formats.yaml { };
in
{
options = {
services.litellm = {
enable = lib.mkEnableOption "LiteLLM server";
package = lib.mkPackageOption pkgs "litellm" { };
stateDir = lib.mkOption {
type = types.path;
default = "/var/lib/litellm";
example = "/home/foo";
description = "State directory of LiteLLM.";
};
host = lib.mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
The host address which the LiteLLM server HTTP interface listens to.
'';
};
port = lib.mkOption {
type = types.port;
default = 8080;
example = 11111;
description = ''
Which port the LiteLLM server listens to.
'';
};
settings = lib.mkOption {
type = types.submodule {
freeformType = settingsFormat.type;
options = {
model_list = lib.mkOption {
type = settingsFormat.type;
description = ''
List of supported models on the server, with model-specific configs.
'';
default = [ ];
};
router_settings = lib.mkOption {
type = settingsFormat.type;
description = ''
LiteLLM Router settings
'';
default = { };
};
litellm_settings = lib.mkOption {
type = settingsFormat.type;
description = ''
LiteLLM Module settings
'';
default = { };
};
general_settings = lib.mkOption {
type = settingsFormat.type;
description = ''
LiteLLM Server settings
'';
default = { };
};
environment_variables = lib.mkOption {
type = settingsFormat.type;
description = ''
Environment variables to pass to the Lite
'';
default = { };
};
};
};
default = { };
description = ''
Configuration for LiteLLM.
See <https://docs.litellm.ai/docs/proxy/configs> for more.
'';
};
environment = lib.mkOption {
type = types.attrsOf types.str;
default = {
SCARF_NO_ANALYTICS = "True";
DO_NOT_TRACK = "True";
ANONYMIZED_TELEMETRY = "False";
};
example = ''
{
NO_DOCS="True";
}
'';
description = ''
Extra environment variables for LiteLLM.
'';
};
environmentFile = lib.mkOption {
description = ''
Environment file to be passed to the systemd service.
Useful for passing secrets to the service to prevent them from being
world-readable in the Nix store.
'';
type = lib.types.nullOr lib.types.path;
default = null;
example = "/var/lib/secrets/liteLLMSecrets";
};
openFirewall = lib.mkOption {
type = types.bool;
default = false;
description = ''
Whether to open the firewall for LiteLLM.
This adds `services.litellm.port` to `networking.firewall.allowedTCPPorts`.
'';
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.litellm = {
description = "LLM Gateway to provide model access, fallbacks and spend tracking across 100+ LLMs.";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment = cfg.environment;
serviceConfig =
let
configFile = settingsFormat.generate "config.yaml" cfg.settings;
in
{
ExecStart = "${lib.getExe cfg.package} --host \"${cfg.host}\" --port ${toString cfg.port} --config ${configFile}";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
WorkingDirectory = cfg.stateDir;
StateDirectory = "litellm";
RuntimeDirectory = "litellm";
RuntimeDirectoryMode = "0755";
PrivateTmp = true;
DynamicUser = true;
DevicePolicy = "closed";
LockPersonality = true;
PrivateUsers = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
UMask = "0077";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
ProtectClock = true;
ProtectProc = "invisible";
};
};
networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
};
meta.maintainers = with lib.maintainers; [ drupol ];
}
@@ -120,6 +120,18 @@ in
RestrictRealtime = true;
SystemCallArchitectures = "native";
UMask = "0077";
CapabilityBoundingSet = "";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
ProtectClock = true;
ProtectProc = "invisible";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
};
};
+134
View File
@@ -0,0 +1,134 @@
{
config,
options,
lib,
pkgs,
...
}:
let
inherit (lib) types;
cfg = config.services.orthanc;
opt = options.services.orthanc;
settingsFormat = pkgs.formats.json { };
in
{
options = {
services.orthanc = {
enable = lib.mkEnableOption "Orthanc server";
package = lib.mkPackageOption pkgs "orthanc" { };
stateDir = lib.mkOption {
type = types.path;
default = "/var/lib/orthanc";
example = "/home/foo";
description = "State directory of Orthanc.";
};
environment = lib.mkOption {
type = types.attrsOf types.str;
default = {
};
example = ''
{
ORTHANC_NAME = "Orthanc server";
}
'';
description = ''
Extra environment variables
For more details see <https://orthanc.uclouvain.be/book/users/configuration.html>
'';
};
environmentFile = lib.mkOption {
description = ''
Environment file to be passed to the systemd service.
Useful for passing secrets to the service to prevent them from being
world-readable in the Nix store.
'';
type = lib.types.nullOr lib.types.path;
default = null;
example = "/var/lib/secrets/orthancSecrets";
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
};
default = {
HttpPort = lib.mkDefault 8042;
IndexDirectory = lib.mkDefault "/var/lib/orthanc/";
StorageDirectory = lib.mkDefault "/var/lib/orthanc/";
};
example = {
Name = "My Orthanc Server";
HttpPort = 12345;
};
description = ''
Configuration written to a json file that is read by orthanc.
See <https://orthanc.uclouvain.be/book/index.html> for more.
'';
};
openFirewall = lib.mkOption {
type = types.bool;
default = false;
description = ''
Whether to open the firewall for Orthanc.
This adds `services.orthanc.settings.HttpPort` to `networking.firewall.allowedTCPPorts`.
'';
};
};
};
config = lib.mkIf cfg.enable {
services.orthanc.settings = options.services.orthanc.settings.default;
systemd.services.orthanc = {
description = "Orthanc is a lightweight, RESTful DICOM server for healthcare and medical research";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment = cfg.environment;
serviceConfig =
let
config-json = settingsFormat.generate "orthanc-config.json" (cfg.settings);
in
{
ExecStart = "${lib.getExe cfg.package} ${config-json}";
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
WorkingDirectory = cfg.stateDir;
BindReadOnlyPaths = [
"-/etc/localtime"
];
StateDirectory = "orthanc";
RuntimeDirectory = "orthanc";
RuntimeDirectoryMode = "0755";
PrivateTmp = true;
DynamicUser = true;
DevicePolicy = "closed";
LockPersonality = true;
PrivateUsers = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
};
networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.settings.HttpPort ]; };
# Orthanc requires /etc/localtime to be present
time.timeZone = lib.mkDefault "UTC";
};
meta.maintainers = with lib.maintainers; [ drupol ];
}
+1 -1
View File
@@ -69,7 +69,7 @@ in
validateSettings = mkOption {
type = types.bool;
default = true;
description = "Weither to run renovate's config validator on the built configuration.";
description = "Whether to run renovate's config validator on the built configuration.";
};
settings = mkOption {
type = json.type;
@@ -68,6 +68,7 @@ in
systemd.services."glances" = {
description = "Glances";
documentation = [ "man:glances(1)" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
+15 -8
View File
@@ -603,9 +603,10 @@ in
''
)
+ ''
# clear cache after update (before migrations)
OLD_VERSION=$(cat ${cfg.dataDir}/version)
if [[ $OLD_VERSION != "${package.version}" ]]; then
# clear cache if package has changed (cache may contain cached paths
# to the old package)
OLD_PACKAGE=$(cat ${cfg.dataDir}/package)
if [[ $OLD_PACKAGE != "${package}" ]]; then
rm -r ${cfg.dataDir}/cache/*
fi
@@ -616,16 +617,21 @@ in
echo "${lib.boolToString cfg.enableOneMinutePolling}" > ${cfg.dataDir}/one_minute_enabled
fi
# migrate db
${artisanWrapper}/bin/librenms-artisan migrate --force --no-interaction
# regenerate cache after migrations after update
# migrate db if package version has changed
# not necessary for every package change
OLD_VERSION=$(cat ${cfg.dataDir}/version)
if [[ $OLD_VERSION != "${package.version}" ]]; then
${artisanWrapper}/bin/librenms-artisan migrate --force --no-interaction
echo "${package.version}" > ${cfg.dataDir}/version
fi
# regenerate cache if package has changed
if [[ $OLD_PACKAGE != "${package}" ]]; then
${artisanWrapper}/bin/librenms-artisan view:clear
${artisanWrapper}/bin/librenms-artisan optimize:clear
${artisanWrapper}/bin/librenms-artisan view:cache
${artisanWrapper}/bin/librenms-artisan optimize
echo "${package.version}" > ${cfg.dataDir}/version
echo "${package}" > ${cfg.dataDir}/package
fi
'';
};
@@ -693,6 +699,7 @@ in
"d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/.env 0600 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/version 0600 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/package 0600 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/one_minute_enabled 0600 ${cfg.user} ${cfg.group} - -"
"f ${cfg.dataDir}/config.json 0600 ${cfg.user} ${cfg.group} - -"
"d ${cfg.dataDir}/storage 0700 ${cfg.user} ${cfg.group} - -"
+8 -12
View File
@@ -28,7 +28,7 @@ let
cfg = config.services.thanos;
nullOpt = type: description: mkOption {
type = types.nullOr type;
type = if type.check null then type else types.nullOr type;
default = null;
description = description;
};
@@ -85,11 +85,7 @@ let
};
};
toYAML = name: attrs: pkgs.runCommand name {
preferLocalBuild = true;
json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
nativeBuildInputs = [ pkgs.remarshal ];
} "json2yaml -i $json -o $out";
format = pkgs.formats.yaml {};
thanos = cmd: "${cfg.package}/bin/thanos ${cmd}" +
(let args = cfg.${cmd}.arguments;
@@ -144,10 +140,10 @@ let
option = mkOption {
type = with types; nullOr str;
default = if cfg.tracing.config == null then null
else toString (toYAML "tracing.yaml" cfg.tracing.config);
else toString (format.generate "tracing.yaml" cfg.tracing.config);
defaultText = literalExpression ''
if config.services.thanos.<cmd>.tracing.config == null then null
else toString (toYAML "tracing.yaml" config.services.thanos.<cmd>.tracing.config);
else toString (format.generate "tracing.yaml" config.services.thanos.<cmd>.tracing.config);
'';
description = ''
Path to YAML file that contains tracing configuration.
@@ -160,7 +156,7 @@ let
tracing.config =
{
toArgs = _opt: _attrs: [];
option = nullOpt types.attrs ''
option = nullOpt format.type ''
Tracing configuration.
When not `null` the attribute set gets converted to
@@ -209,10 +205,10 @@ let
option = mkOption {
type = with types; nullOr str;
default = if cfg.objstore.config == null then null
else toString (toYAML "objstore.yaml" cfg.objstore.config);
else toString (format.generate "objstore.yaml" cfg.objstore.config);
defaultText = literalExpression ''
if config.services.thanos.<cmd>.objstore.config == null then null
else toString (toYAML "objstore.yaml" config.services.thanos.<cmd>.objstore.config);
else toString (format.generate "objstore.yaml" config.services.thanos.<cmd>.objstore.config);
'';
description = ''
Path to YAML file that contains object store configuration.
@@ -225,7 +221,7 @@ let
objstore.config =
{
toArgs = _opt: _attrs: [];
option = nullOpt types.attrs ''
option = nullOpt format.type ''
Object store configuration.
When not `null` the attribute set gets converted to
@@ -218,6 +218,7 @@ in
in
lib.nameValuePair "tahoe.introducer-${node}" {
description = "Tahoe LAFS node ${node}";
documentation = [ "info:tahoe-lafs" ];
wantedBy = [ "multi-user.target" ];
path = [ settings.package ];
restartTriggers = [
@@ -329,6 +330,7 @@ in
in
lib.nameValuePair "tahoe.${node}" {
description = "Tahoe LAFS node ${node}";
documentation = [ "info:tahoe-lafs" ];
wantedBy = [ "multi-user.target" ];
path = [ settings.package ];
restartTriggers = [
@@ -305,6 +305,12 @@ in
description = "Avahi mDNS/DNS-SD Stack";
wantedBy = [ "multi-user.target" ];
requires = [ "avahi-daemon.socket" ];
documentation = [
"man:avahi-daemon(8)"
"man:avahi-daemon.conf(5)"
"man:avahi.hosts(5)"
"man:avahi.service(5)"
];
# Make NSS modules visible so that `avahi_nss_support ()' can
# return a sensible value.
@@ -10,9 +10,11 @@ let
certificateFile = lib.mkOption {
type = with lib.types; nullOr path;
description = ''
Cert.pem file.
Account certificate file, necessary to create, delete and manage tunnels. It can be obtained by running `cloudflared login`.
See [Cert.pem](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#certpem).
Note that this is **necessary** for a fully declarative set up, as routes can not otherwise be created outside of the Cloudflare interface.
See [Cert.pem](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#certpem) for information about the file, and [Tunnel permissions](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/local-management/tunnel-permissions/) for a comparison between the account certificate and the tunnel credentials file.
'';
default = null;
};
@@ -301,12 +303,6 @@ in
};
config = lib.mkIf cfg.enable {
assertions = lib.mapAttrsToList (name: tunnel: {
assertion =
tunnel.ingress == { } || (cfg.certificateFile != null || tunnel.certificateFile != null);
message = "Cloudflare Tunnel ${name} has a declarative configuration, but no certificate file was defined.";
}) cfg.tunnels;
systemd.targets = lib.mapAttrs' (
name: tunnel:
lib.nameValuePair "cloudflared-tunnel-${name}" {
@@ -209,6 +209,8 @@ in
in
{ description = "DHCP Client";
documentation = [ "man:dhcpcd(8)" ];
wantedBy = [ "multi-user.target" ] ++ lib.optional (!hasDefaultGatewaySet) "network-online.target";
wants = [ "network.target" "resolvconf.service" ];
after = [ "resolvconf.service" ];
@@ -149,6 +149,7 @@ in
systemd.services.gnunet = {
description = "GNUnet";
documentation = [ "info:gnunet" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ config.environment.etc."gnunet.conf".source ];
@@ -114,6 +114,10 @@ in
config = mkIf cfg.enable {
systemd.services.gvpe = {
description = "GNU Virtual Private Ethernet node";
documentation = [
"info:gvpe"
"man:gvpe(8)"
];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
+2 -2
View File
@@ -22,8 +22,8 @@ let
nativeBuildInputs = [ nats-server ];
}
''
nats-server --config "${configFile}" -t
ln -s "${configFile}" "$out"
nats-server --config "${file}" -t
ln -s "${file}" "$out"
''
) { };
+4 -21
View File
@@ -4,25 +4,8 @@ let
cfg = cfgs.ncdns;
dataDir = "/var/lib/ncdns";
username = "ncdns";
valueType = with lib.types; oneOf [ int str bool path ]
// { description = "setting type (integer, string, bool or path)"; };
configType = with lib.types; attrsOf (nullOr (either valueType configType))
// { description = ''
ncdns.conf configuration type. The format consists of an
attribute set of settings. Each setting can be either `null`,
a value or an attribute set. The allowed values are integers,
strings, booleans or paths.
'';
};
configFile = pkgs.runCommand "ncdns.conf"
{ json = builtins.toJSON cfg.settings;
passAsFile = [ "json" ];
}
"${pkgs.remarshal}/bin/json2toml < $jsonPath > $out";
format = pkgs.formats.toml {};
defaultFiles = {
public = "${dataDir}/bit.key";
@@ -35,7 +18,7 @@ let
needsKeygen = lib.all lib.id (lib.flip lib.mapAttrsToList cfg.dnssec.keys
(n: v: v == lib.getAttr n defaultFiles));
mkDefaultAttrs = lib.mapAttrs (n: v: lib.mkDefault v);
mkDefaultAttrs = lib.mapAttrs (_n: v: lib.mkDefault v);
in
@@ -160,7 +143,7 @@ in
};
settings = lib.mkOption {
type = configType;
type = format.type;
default = { };
example = lib.literalExpression ''
{ # enable webserver
@@ -257,7 +240,7 @@ in
User = "ncdns";
StateDirectory = "ncdns";
Restart = "on-failure";
ExecStart = "${pkgs.ncdns}/bin/ncdns -conf=${configFile}";
ExecStart = "${pkgs.ncdns}/bin/ncdns -conf=${format.generate "ncdns.conf" cfg.settings}";
};
preStart = lib.optionalString (cfg.dnssec.enable && needsKeygen) ''
@@ -229,6 +229,7 @@ in
systemd.services.privoxy = {
description = "Filtering web proxy";
documentation = [ "man:privoxy(8)" ];
after = [
"network.target"
"nss-lookup.target"
+37 -2
View File
@@ -11,6 +11,7 @@ let
(builtins.toJSON cfg.settings);
limiterSettingsFile = (pkgs.formats.toml { }).generate "limiter.toml" cfg.limiterSettings;
faviconsSettingsFile = (pkgs.formats.toml { }).generate "favicons.toml" cfg.faviconsSettings;
generateConfig = ''
cd ${runDir}
@@ -143,6 +144,35 @@ in
'';
};
faviconsSettings = mkOption {
type = types.attrsOf settingType;
default = { };
example = literalExpression ''
{
favicons = {
cfg_schema = 1;
cache = {
db_url = "/run/searx/faviconcache.db";
HOLD_TIME = 5184000;
LIMIT_TOTAL_BYTES = 2147483648;
BLOB_MAX_BYTES = 40960;
MAINTENANCE_MODE = "auto";
MAINTENANCE_PERIOD = 600;
};
};
}
'';
description = ''
Favicons settings for SearXNG.
::: {.note}
For available settings, see the SearXNG
[schema file](https://github.com/searxng/searxng/blob/master/searx/favicons/favicons.toml).
:::
'';
};
package = mkPackageOption pkgs "searxng" { };
runInUwsgi = mkOption {
@@ -263,8 +293,13 @@ in
port = 0;
};
environment.etc."searxng/limiter.toml" = lib.mkIf (cfg.limiterSettings != { }) {
source = limiterSettingsFile;
environment.etc = {
"searxng/limiter.toml" = lib.mkIf (cfg.limiterSettings != { }) {
source = limiterSettingsFile;
};
"searxng/favicons.toml" = lib.mkIf (cfg.faviconsSettings != { }) {
source = faviconsSettingsFile;
};
};
};
@@ -142,6 +142,7 @@ in
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
documentation = [ "man:soju(1)" ];
serviceConfig = {
DynamicUser = true;
Restart = "always";
@@ -366,6 +366,10 @@ in
("tinc.${network}")
(let version = getVersion data.package; in {
description = "Tinc Daemon - ${network}";
documentation = [
"info:tinc"
"man:tincd(8)"
];
wantedBy = [ "multi-user.target" ];
path = [ data.package ];
reloadTriggers = mkIf (versionAtLeast version "1.1pre") [ (builtins.toJSON etcConfig) ];
@@ -82,6 +82,7 @@ in
systemd.services.atd = {
description = "Job Execution Daemon (atd)";
documentation = [ "man:atd(8)" ];
wantedBy = [ "multi-user.target" ];
path = [ at ];
@@ -169,6 +169,7 @@ in
systemd.services.clamav-daemon = lib.mkIf cfg.daemon.enable {
description = "ClamAV daemon (clamd)";
documentation = [ "man:clamd(8)" ];
after = lib.optionals cfg.updater.enable [ "clamav-freshclam.service" ];
wants = lib.optionals cfg.updater.enable [ "clamav-freshclam.service" ];
wantedBy = [ "multi-user.target" ];
@@ -199,6 +200,7 @@ in
systemd.services.clamav-freshclam = lib.mkIf cfg.updater.enable {
description = "ClamAV virus database updater (freshclam)";
documentation = [ "man:freshclam(1)" ];
restartTriggers = [ freshclamConfigFile ];
requires = [ "network-online.target" ];
after = [ "network-online.target" ];
@@ -276,6 +278,7 @@ in
systemd.services.clamdscan = lib.mkIf cfg.scanner.enable {
description = "ClamAV virus scanner";
documentation = [ "man:clamdscan(1)" ];
after = lib.optionals cfg.updater.enable [ "clamav-freshclam.service" ];
wants = lib.optionals cfg.updater.enable [ "clamav-freshclam.service" ];
@@ -48,6 +48,7 @@ in
config = lib.mkIf cfg.enable {
systemd.services.endlessh = {
description = "SSH tarpit";
documentation = [ "man:endlessh(1)" ];
requires = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig =
-154
View File
@@ -1,154 +0,0 @@
# This module defines global configuration for Haka.
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.haka;
haka = cfg.package;
hakaConf = pkgs.writeText "haka.conf" ''
[general]
configuration = ${
if lib.strings.hasPrefix "/" cfg.configFile then
"${cfg.configFile}"
else
"${haka}/share/haka/sample/${cfg.configFile}"
}
${lib.optionalString (builtins.lessThan 0 cfg.threads) "thread = ${cfg.threads}"}
[packet]
${lib.optionalString cfg.pcap ''module = "packet/pcap"''}
${lib.optionalString cfg.nfqueue ''module = "packet/nqueue"''}
${lib.optionalString cfg.dump.enable ''dump = "yes"''}
${lib.optionalString cfg.dump.enable ''dump_input = "${cfg.dump.input}"''}
${lib.optionalString cfg.dump.enable ''dump_output = "${cfg.dump.output}"''}
interfaces = "${lib.strings.concatStringsSep "," cfg.interfaces}"
[log]
# Select the log module
module = "log/syslog"
# Set the default logging level
#level = "info,packet=debug"
[alert]
# Select the alert module
module = "alert/syslog"
# Disable alert on standard output
#alert_on_stdout = no
# alert/file module option
#file = "/dev/null"
'';
in
{
###### interface
options = {
services.haka = {
enable = lib.mkEnableOption "Haka";
package = lib.mkPackageOption pkgs "haka" { };
configFile = lib.mkOption {
default = "empty.lua";
example = "/srv/haka/myfilter.lua";
type = lib.types.str;
description = ''
Specify which configuration file Haka uses.
It can be absolute path or a path relative to the sample directory of
the haka git repo.
'';
};
interfaces = lib.mkOption {
default = [ "eth0" ];
example = [ "any" ];
type = with lib.types; listOf str;
description = ''
Specify which interface(s) Haka listens to.
Use 'any' to listen to all interfaces.
'';
};
threads = lib.mkOption {
default = 0;
example = 4;
type = lib.types.int;
description = ''
The number of threads that will be used.
All system threads are used by default.
'';
};
pcap = lib.mkOption {
default = true;
type = lib.types.bool;
description = "Whether to enable pcap";
};
nfqueue = lib.mkEnableOption "nfqueue";
dump.enable = lib.mkEnableOption "dump";
dump.input = lib.mkOption {
default = "/tmp/input.pcap";
example = "/path/to/file.pcap";
type = lib.types.path;
description = "Path to file where incoming packets are dumped";
};
dump.output = lib.mkOption {
default = "/tmp/output.pcap";
example = "/path/to/file.pcap";
type = lib.types.path;
description = "Path to file where outgoing packets are dumped";
};
};
};
###### implementation
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.pcap != cfg.nfqueue;
message = "either pcap or nfqueue can be enabled, not both.";
}
{
assertion = cfg.nfqueue -> !cfg.dump.enable;
message = "dump can only be used with nfqueue.";
}
{
assertion = cfg.interfaces != [ ];
message = "at least one interface must be specified.";
}
];
environment.systemPackages = [ haka ];
systemd.services.haka = {
description = "Haka";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${haka}/bin/haka -c ${hakaConf}";
ExecStop = "${haka}/bin/hakactl stop";
User = "root";
Type = "forking";
};
};
};
}
+2 -2
View File
@@ -790,12 +790,12 @@ in
++ concatLists (
flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) (
oauth2: oauth2Cfg:
(optionals (cfg.provision.extraJsonFile == null) (
(optional (cfg.provision.extraJsonFile == null) (
assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (
attrNames oauth2Cfg.scopeMaps
)
))
++ (optionals (cfg.provision.extraJsonFile == null) (
++ (optional (cfg.provision.extraJsonFile == null) (
assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (
attrNames oauth2Cfg.supplementaryScopeMaps
)
@@ -46,6 +46,10 @@ in
users.groups.munge = { };
systemd.services.munged = {
documentation = [
"man:munged(8)"
"man:mungekey(8)"
];
wantedBy = [ "multi-user.target" ];
wants = [
"network-online.target"
@@ -116,6 +116,7 @@ in
systemd.services.physlock = {
enable = true;
documentation = [ "man:physlock(1)" ];
description = "Physlock";
wantedBy =
lib.optional cfg.lockOn.suspend "suspend.target"
+1
View File
@@ -119,6 +119,7 @@ in
{
sks-db = {
description = "SKS database server";
documentation = [ "man:sks(8)" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
+1
View File
@@ -1324,6 +1324,7 @@ in
systemd.services.tor = {
description = "Tor Daemon";
documentation = [ "man:tor(8)" ];
path = [ pkgs.tor ];
wantedBy = [ "multi-user.target" ];
+6 -6
View File
@@ -95,7 +95,7 @@ let
package = mkOption {
type = types.package;
description = "Akkoma frontend package.";
example = literalExpression "pkgs.akkoma-frontends.akkoma-fe";
example = literalExpression "pkgs.akkoma-fe";
};
name = mkOption {
@@ -520,12 +520,12 @@ in {
type = with types; attrsOf (submodule frontend);
default = {
primary = {
package = pkgs.akkoma-frontends.akkoma-fe;
package = pkgs.akkoma-fe;
name = "akkoma-fe";
ref = "stable";
};
admin = {
package = pkgs.akkoma-frontends.admin-fe;
package = pkgs.akkoma-admin-fe;
name = "admin-fe";
ref = "stable";
};
@@ -533,12 +533,12 @@ in {
defaultText = literalExpression ''
{
primary = {
package = pkgs.akkoma-frontends.akkoma-fe;
package = pkgs.akkoma-fe;
name = "akkoma-fe";
ref = "stable";
};
admin = {
package = pkgs.akkoma-frontends.admin-fe;
package = pkgs.akkoma-admin-fe;
name = "admin-fe";
ref = "stable";
};
@@ -557,7 +557,7 @@ in {
default = null;
example = literalExpression ''
{
"emoji/blobs.gg" = pkgs.akkoma-emoji.blobs_gg;
"emoji/blobs.gg" = pkgs.blobs_gg;
"static/terms-of-service.html" = pkgs.writeText "terms-of-service.html" '''
''';
@@ -73,7 +73,7 @@ in
default = null;
example = "/run/secrets/changedetection-io.env";
description = ''
Securely pass environment variabels to changedetection-io.
Securely pass environment variables to changedetection-io.
This can be used to set for example a frontend password reproducible via `SALTED_PASS`
which convinetly also deactivates nags about the hosted version.
@@ -196,6 +196,10 @@ in
}
];
systemd.services."phpfpm-flarum" = {
restartTriggers = [ cfg.package ];
};
systemd.services.flarum-install = {
description = "Flarum installation";
requiredBy = [ "phpfpm-flarum.service" ];
@@ -128,7 +128,11 @@ in
in
{
description = "hledger-web - web-app for the hledger accounting tool.";
documentation = [ "https://hledger.org/hledger-web.html" ];
documentation = [
"info:hledger-web"
"man:hledger-web(1)"
"https://hledger.org/hledger-web.html"
];
wantedBy = [ "multi-user.target" ];
after = [ "networking.target" ];
serviceConfig = mkMerge [
+18 -16
View File
@@ -195,7 +195,7 @@ in {
affect other virtualHosts running on your nginx instance, if any.
Alternatively you can configure a reverse-proxy of your choice to serve these paths:
`/ -> $(nix-instantiate --eval '<nixpkgs>' -A mastodon.outPath)/public`
`/ -> ''${pkgs.mastodon}/public`
`/ -> 127.0.0.1:{{ webPort }} `(If there was no file in the directory above.)
@@ -324,6 +324,21 @@ in {
type = lib.types.str;
};
vapidPrivateKeyFile = lib.mkOption {
description = ''
Path to file containing the private key used for Web Push
Voluntary Application Server Identification. A new keypair can
be generated by running:
`nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys`
If this file does not exist, it will be created with a new
private key.
'';
default = "/var/lib/mastodon/secrets/vapid-private-key";
type = lib.types.str;
};
localDomain = lib.mkOption {
description = "The domain serving your Mastodon instance.";
example = "social.example.org";
@@ -401,21 +416,6 @@ in {
type = lib.types.str;
};
vapidPrivateKeyFile = lib.mkOption {
description = ''
Path to file containing the private key used for Web Push
Voluntary Application Server Identification. A new keypair can
be generated by running:
`nix build -f '<nixpkgs>' mastodon; cd result; bin/rake webpush:generate_keys`
If this file does not exist, it will be created with a new
private key.
'';
default = "/var/lib/mastodon/secrets/vapid-private-key";
type = lib.types.str;
};
trustedProxy = lib.mkOption {
description = ''
You need to set it to the IP from which your reverse proxy sends requests to Mastodon's web process,
@@ -945,6 +945,7 @@ in {
enable = true;
hostname = lib.mkDefault "${cfg.localDomain}";
};
services.redis.servers.mastodon = lib.mkIf redisActuallyCreateLocally (lib.mkMerge [
{
enable = true;
@@ -953,6 +954,7 @@ in {
port = cfg.redis.port;
})
]);
services.postgresql = lib.mkIf databaseActuallyCreateLocally {
enable = true;
ensureUsers = [
@@ -282,6 +282,7 @@ in {
Type = "simple";
User = cfg.user;
Group = cfg.group;
Environment = ["DOTNET_USE_POLLING_FILE_WATCHER=1"];
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
StateDirectory = "slskd"; # Creates /var/lib/slskd and manages permissions
ExecStart = "${cfg.package}/bin/slskd --app-dir /var/lib/slskd --config ${configurationYaml}";
@@ -254,6 +254,7 @@ in {
after = [ "network.target" ];
wantedBy = [ "phpfpm.target" ];
partOf = [ "phpfpm.target" ];
documentation = [ "man:php-fpm(8)" ];
serviceConfig = let
cfgFile = fpmCfgFile pool poolOpts;
iniFile = phpIni poolOpts;
+10 -40
View File
@@ -4,48 +4,18 @@ with lib;
let
cfg = config.services.traefik;
jsonValue = with types;
let
valueType = nullOr (oneOf [
bool
int
float
str
(lazyAttrsOf valueType)
(listOf valueType)
]) // {
description = "JSON value";
emptyValue.value = { };
};
in valueType;
format = pkgs.formats.toml {};
dynamicConfigFile = if cfg.dynamicConfigFile == null then
pkgs.runCommand "config.toml" {
buildInputs = [ pkgs.remarshal ];
preferLocalBuild = true;
} ''
remarshal -if json -of toml \
< ${
pkgs.writeText "dynamic_config.json"
(builtins.toJSON cfg.dynamicConfigOptions)
} \
> $out
''
format.generate "config.toml" cfg.dynamicConfigOptions
else
cfg.dynamicConfigFile;
staticConfigFile = if cfg.staticConfigFile == null then
pkgs.runCommand "config.toml" {
buildInputs = [ pkgs.yj ];
preferLocalBuild = true;
} ''
yj -jt -i \
< ${
pkgs.writeText "static_config.json" (builtins.toJSON
(recursiveUpdate cfg.staticConfigOptions {
providers.file.filename = "${dynamicConfigFile}";
}))
} \
> $out
''
format.generate "config.toml" (recursiveUpdate cfg.staticConfigOptions {
providers.file.filename = "${dynamicConfigFile}";
})
else
cfg.staticConfigFile;
@@ -71,7 +41,7 @@ in {
description = ''
Static configuration for Traefik.
'';
type = jsonValue;
type = format.type;
default = { entryPoints.http.address = ":80"; };
example = {
entryPoints.web.address = ":8080";
@@ -95,7 +65,7 @@ in {
description = ''
Dynamic configuration for Traefik.
'';
type = jsonValue;
type = format.type;
default = { };
example = {
http.routers.router1 = {
+2 -13
View File
@@ -33,19 +33,8 @@ let
c.plugins or cfg.plugins;
plugins = unique plugins';
hasPython = v: filter (n: n == "python${v}") plugins != [ ];
hasPython2 = hasPython "2";
hasPython3 = hasPython "3";
python =
if hasPython2 && hasPython3 then
throw "`plugins` attribute in uWSGI configuration shouldn't contain both python2 and python3"
else if hasPython2 then
cfg.package.python2
else if hasPython3 then
cfg.package.python3
else
null;
hasPython3 = filter (n: n == "python3") plugins != [ ];
python = if hasPython3 then cfg.package.python3 else null;
pythonEnv = python.withPackages (c.pythonPackages or (self: [ ]));
+2 -1
View File
@@ -10,7 +10,7 @@ let
cfg = config.systemd.repart;
initrdCfg = config.boot.initrd.systemd.repart;
format = pkgs.formats.ini { };
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format (
lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions
@@ -93,6 +93,7 @@ in
str
int
bool
(listOf str)
])
);
default = { };
@@ -166,6 +166,7 @@ in
in
nameValuePair "btrfs-scrub-${fs'}" {
description = "btrfs scrub on ${fs}";
documentation = [ "man:btrfs-scrub(8)" ];
# scrub prevents suspend2ram or proper shutdown
conflicts = [
"shutdown.target"
+1
View File
@@ -51,6 +51,7 @@ in
config = mkIf (cfg.enable) {
systemd.services = {
powertop = {
documentation = [ "man:powertop(8)" ];
wantedBy = [ "multi-user.target" ];
after = [ "multi-user.target" ];
description = "Powertop tunings";
+35
View File
@@ -159,6 +159,34 @@ in
which the prune will occur.
'';
};
randomizedDelaySec = mkOption {
default = "0";
type = types.singleLineStr;
example = "45min";
description = ''
Add a randomized delay before each auto prune.
The delay will be chosen between zero and this value.
This value must be a time span in the format specified by
{manpage}`systemd.time(7)`
'';
};
persistent = mkOption {
default = true;
type = types.bool;
example = false;
description = ''
Takes a boolean argument. If true, the time when the service
unit was last triggered is stored on disk. When the timer is
activated, the service unit is triggered immediately if it
would have been triggered at least once during the time when
the timer was inactive. Such triggering is nonetheless
subject to the delay imposed by RandomizedDelaySec=. This is
useful to catch up on missed runs of the service when the
system was powered down.
'';
};
};
package = mkPackageOption pkgs "docker" { };
@@ -254,6 +282,13 @@ in
requires = [ "docker.service" ];
};
systemd.timers.docker-prune = mkIf cfg.autoPrune.enable {
timerConfig = {
RandomizedDelaySec = cfg.autoPrune.randomizedDelaySec;
Persistent = cfg.autoPrune.persistent;
};
};
assertions = [
{ assertion = cfg.enableNvidia && pkgs.stdenv.hostPlatform.isx86_64 -> config.hardware.graphics.enable32Bit or false;
message = "Option enableNvidia on x86_64 requires 32-bit support libraries";
+1 -1
View File
@@ -87,7 +87,7 @@ with lib;
# ec2-get-console-output.
echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" > /dev/console
for i in /etc/ssh/ssh_host_*_key.pub; do
${config.programs.ssh.package}/bin/ssh-keygen -l -f "$i" || true > /dev/console
${config.programs.ssh.package}/bin/ssh-keygen -l -f "$i" > /dev/console || true
done
echo "-----END SSH HOST KEY FINGERPRINTS-----" > /dev/console
'';
+1 -1
View File
@@ -363,7 +363,7 @@ in
type = types.bool;
default = true;
description = ''
Weither to configure OpenSSH to use the [SSH Proxy](https://libvirt.org/ssh-proxy.html).
Whether to configure OpenSSH to use the [SSH Proxy](https://libvirt.org/ssh-proxy.html).
'';
};
};
+43 -46
View File
@@ -1,58 +1,55 @@
import ./make-test-python.nix (
{ ... }:
{
name = "acme-dns";
{
name = "acme-dns";
nodes.machine =
{ pkgs, ... }:
{
services.acme-dns = {
enable = true;
settings = {
general = rec {
domain = "acme-dns.home.arpa";
nsname = domain;
nsadmin = "admin.home.arpa";
records = [
"${domain}. A 127.0.0.1"
"${domain}. AAAA ::1"
"${domain}. NS ${domain}."
];
};
logconfig.loglevel = "debug";
nodes.machine =
{ pkgs, ... }:
{
services.acme-dns = {
enable = true;
settings = {
general = rec {
domain = "acme-dns.home.arpa";
nsname = domain;
nsadmin = "admin.home.arpa";
records = [
"${domain}. A 127.0.0.1"
"${domain}. AAAA ::1"
"${domain}. NS ${domain}."
];
};
logconfig.loglevel = "debug";
};
environment.systemPackages = with pkgs; [
curl
bind
];
};
environment.systemPackages = with pkgs; [
curl
bind
];
};
testScript = ''
import json
testScript = ''
import json
machine.wait_for_unit("acme-dns.service")
machine.wait_for_open_port(53) # dns
machine.wait_for_open_port(8080) # http api
machine.wait_for_unit("acme-dns.service")
machine.wait_for_open_port(53) # dns
machine.wait_for_open_port(8080) # http api
result = machine.succeed("curl --fail -X POST http://localhost:8080/register")
print(result)
result = machine.succeed("curl --fail -X POST http://localhost:8080/register")
print(result)
registration = json.loads(result)
registration = json.loads(result)
machine.succeed(f'dig -t TXT @localhost {registration["fulldomain"]} | grep "SOA" | grep "admin.home.arpa"')
machine.succeed(f'dig -t TXT @localhost {registration["fulldomain"]} | grep "SOA" | grep "admin.home.arpa"')
# acme-dns exspects a TXT value string length of exactly 43 chars
txt = "___dummy_validation_token_for_txt_record___"
# acme-dns exspects a TXT value string length of exactly 43 chars
txt = "___dummy_validation_token_for_txt_record___"
machine.succeed(
"curl --fail -X POST http://localhost:8080/update "
+ f' -H "X-Api-User: {registration["username"]}"'
+ f' -H "X-Api-Key: {registration["password"]}"'
+ f' -d \'{{"subdomain":"{registration["subdomain"]}", "txt":"{txt}"}}\'''
)
machine.succeed(
"curl --fail -X POST http://localhost:8080/update "
+ f' -H "X-Api-User: {registration["username"]}"'
+ f' -H "X-Api-Key: {registration["password"]}"'
+ f' -d \'{{"subdomain":"{registration["subdomain"]}", "txt":"{txt}"}}\'''
)
assert txt in machine.succeed(f'dig -t TXT +short @localhost {registration["fulldomain"]}')
'';
}
)
assert txt in machine.succeed(f'dig -t TXT +short @localhost {registration["fulldomain"]}')
'';
}
+14 -16
View File
@@ -1,18 +1,16 @@
import ./make-test-python.nix (
{ lib, ... }:
{
name = "actual";
meta.maintainers = [ lib.maintainers.oddlama ];
{ lib, ... }:
{
name = "actual";
meta.maintainers = [ lib.maintainers.oddlama ];
nodes.machine =
{ ... }:
{
services.actual.enable = true;
};
nodes.machine =
{ ... }:
{
services.actual.enable = true;
};
testScript = ''
machine.wait_for_open_port(3000)
machine.succeed("curl -fvvv -Ls http://localhost:3000/ | grep 'Actual'")
'';
}
)
testScript = ''
machine.wait_for_open_port(3000)
machine.succeed("curl -fvvv -Ls http://localhost:3000/ | grep 'Actual'")
'';
}
+41 -43
View File
@@ -1,49 +1,47 @@
import ./make-test-python.nix (
{ pkgs, ... }:
{ pkgs, ... }:
let
hello-world = pkgs.writeText "hello-world" ''
{-# OPTIONS --guardedness #-}
open import IO
open import Level
let
hello-world = pkgs.writeText "hello-world" ''
{-# OPTIONS --guardedness #-}
open import IO
open import Level
main = run {0} (putStrLn "Hello World!")
'';
in
{
name = "agda";
meta = with pkgs.lib.maintainers; {
maintainers = [
alexarice
turion
main = run {0} (putStrLn "Hello World!")
'';
in
{
name = "agda";
meta = with pkgs.lib.maintainers; {
maintainers = [
alexarice
turion
];
};
nodes.machine =
{ pkgs, ... }:
{
environment.systemPackages = [
(pkgs.agda.withPackages {
pkgs = p: [ p.standard-library ];
})
];
virtualisation.memorySize = 2000; # Agda uses a lot of memory
};
nodes.machine =
{ pkgs, ... }:
{
environment.systemPackages = [
(pkgs.agda.withPackages {
pkgs = p: [ p.standard-library ];
})
];
virtualisation.memorySize = 2000; # Agda uses a lot of memory
};
testScript = ''
# Minimal script that typechecks
machine.succeed("touch TestEmpty.agda")
machine.succeed("agda TestEmpty.agda")
testScript = ''
# Minimal script that typechecks
machine.succeed("touch TestEmpty.agda")
machine.succeed("agda TestEmpty.agda")
# Hello world
machine.succeed(
"cp ${hello-world} HelloWorld.agda"
)
machine.succeed("agda -l standard-library -i . -c HelloWorld.agda")
# Check execution
assert "Hello World!" in machine.succeed(
"./HelloWorld"
), "HelloWorld does not run properly"
'';
}
)
# Hello world
machine.succeed(
"cp ${hello-world} HelloWorld.agda"
)
machine.succeed("agda -l standard-library -i . -c HelloWorld.agda")
# Check execution
assert "Hello World!" in machine.succeed(
"./HelloWorld"
), "HelloWorld does not run properly"
'';
}
+25 -27
View File
@@ -1,32 +1,30 @@
import ./make-test-python.nix (
{ pkgs, ... }:
{
name = "airsonic";
meta = with pkgs.lib.maintainers; {
maintainers = [ sumnerevans ];
{ pkgs, ... }:
{
name = "airsonic";
meta = with pkgs.lib.maintainers; {
maintainers = [ sumnerevans ];
};
nodes.machine =
{ pkgs, ... }:
{
services.airsonic = {
enable = true;
maxMemory = 800;
};
};
nodes.machine =
{ pkgs, ... }:
{
services.airsonic = {
enable = true;
maxMemory = 800;
};
};
testScript = ''
def airsonic_is_up(_) -> bool:
status, _ = machine.execute("curl --fail http://localhost:4040/login")
return status == 0
testScript = ''
def airsonic_is_up(_) -> bool:
status, _ = machine.execute("curl --fail http://localhost:4040/login")
return status == 0
machine.start()
machine.wait_for_unit("airsonic.service")
machine.wait_for_open_port(4040)
machine.start()
machine.wait_for_unit("airsonic.service")
machine.wait_for_open_port(4040)
with machine.nested("Waiting for UI to work"):
retry(airsonic_is_up)
'';
}
)
with machine.nested("Waiting for UI to work"):
retry(airsonic_is_up)
'';
}
+228 -132
View File
@@ -1,149 +1,245 @@
/*
End-to-end test for Akkoma.
# endtoend test for Akkoma
{
lib,
pkgs,
confined ? false,
...
}:
let
inherit ((pkgs.formats.elixirConf { }).lib) mkRaw;
Based in part on nixos/tests/pleroma.
package = pkgs.akkoma;
TODO: Test federation.
*/
import ./make-test-python.nix (
{
pkgs,
package ? pkgs.akkoma,
confined ? false,
...
}:
let
userPassword = "4LKOrGo8SgbPm1a6NclVU5Wb";
tlsCert =
names:
pkgs.runCommand "certificates-${lib.head names}"
{
nativeBuildInputs = with pkgs; [ openssl ];
}
''
mkdir -p $out
openssl req -x509 \
-subj '/CN=${lib.head names}/' -days 49710 \
-addext 'subjectAltName = ${lib.concatStringsSep ", " (map (name: "DNS:${name}") names)}' \
-keyout "$out/key.pem" -newkey ed25519 \
-out "$out/cert.pem" -noenc
'';
provisionUser = pkgs.writers.writeBashBin "provisionUser" ''
set -eu -o errtrace -o pipefail
tlsCertA = tlsCert [
"akkoma-a.nixos.test"
"media.akkoma-a.nixos.test"
];
pleroma_ctl user new jamy jamy@nixos.test --password '${userPassword}' --moderator --admin -y
'';
tlsCertB = tlsCert [
"akkoma-b.nixos.test"
"media.akkoma-b.nixos.test"
];
tlsCert =
pkgs.runCommand "selfSignedCerts"
{
nativeBuildInputs = with pkgs; [ openssl ];
}
''
mkdir -p $out
openssl req -x509 \
-subj '/CN=akkoma.nixos.test/' -days 49710 \
-addext 'subjectAltName = DNS:akkoma.nixos.test' \
-keyout "$out/key.pem" -newkey ed25519 \
-out "$out/cert.pem" -noenc
'';
sendToot = pkgs.writers.writeBashBin "sendToot" ''
set -eu -o errtrace -o pipefail
export REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"
${pkgs.toot}/bin/toot login_cli -i "akkoma.nixos.test" -e "jamy@nixos.test" -p '${userPassword}'
${pkgs.toot}/bin/toot post "hello world Jamy here"
${pkgs.toot}/bin/toot timeline -1 | grep -F -q "hello world Jamy here"
# Test file upload
echo "y" | ${pkgs.toot}/bin/toot upload <(dd if=/dev/zero bs=1024 count=1024 status=none) \
| grep -F -q "https://akkoma.nixos.test:443/media"
'';
checkFe = pkgs.writers.writeBashBin "checkFe" ''
set -eu -o errtrace -o pipefail
testMedia = pkgs.runCommand "blank.png" { nativeBuildInputs = with pkgs; [ imagemagick ]; } ''
magick -size 640x480 canvas:transparent "PNG8:$out"
'';
checkFe = pkgs.writeShellApplication {
name = "checkFe";
runtimeInputs = with pkgs; [ curl ];
text = ''
paths=( / /static/{config,styles}.json /pleroma/admin/ )
for path in "''${paths[@]}"; do
diff \
<(${pkgs.curl}/bin/curl -f -S -s -o /dev/null -w '%{response_code}' "https://akkoma.nixos.test$path") \
<(curl -f -S -s -o /dev/null -w '%{response_code}' "https://$1$path") \
<(echo -n 200)
done
'';
};
hosts = nodes: ''
${nodes.akkoma.networking.primaryIPAddress} akkoma.nixos.test
${nodes.client.networking.primaryIPAddress} client.nixos.test
'';
in
{
name = "akkoma";
nodes = {
client =
{
nodes,
pkgs,
config,
...
}:
{
security.pki.certificateFiles = [ "${tlsCert}/cert.pem" ];
networking.extraHosts = hosts nodes;
};
commonConfig =
{ nodes, ... }:
{
security.pki.certificateFiles = [
"${tlsCertA}/cert.pem"
"${tlsCertB}/cert.pem"
];
akkoma =
{
nodes,
pkgs,
config,
...
}:
{
networking.extraHosts = hosts nodes;
networking.firewall.allowedTCPPorts = [ 443 ];
environment.systemPackages = with pkgs; [ provisionUser ];
systemd.services.akkoma.confinement.enable = confined;
services.akkoma = {
enable = true;
package = package;
config = {
":pleroma" = {
":instance" = {
name = "NixOS test Akkoma server";
description = "NixOS test Akkoma server";
email = "akkoma@nixos.test";
notify_email = "akkoma@nixos.test";
registration_open = true;
};
":media_proxy" = {
enabled = false;
};
"Pleroma.Web.Endpoint" = {
url.host = "akkoma.nixos.test";
};
"Pleroma.Upload" = {
base_url = "https://akkoma.nixos.test:443/media/";
};
};
};
nginx = {
addSSL = true;
sslCertificate = "${tlsCert}/cert.pem";
sslCertificateKey = "${tlsCert}/key.pem";
};
};
services.nginx.enable = true;
services.postgresql.enable = true;
};
networking.extraHosts = ''
${nodes.akkoma-a.networking.primaryIPAddress} akkoma-a.nixos.test media.akkoma-a.nixos.test
${nodes.akkoma-b.networking.primaryIPAddress} akkoma-b.nixos.test media.akkoma-b.nixos.test
${nodes.client-a.networking.primaryIPAddress} client-a.nixos.test
${nodes.client-b.networking.primaryIPAddress} client-b.nixos.test
'';
};
testScript =
{ nodes, ... }:
''
start_all()
akkoma.wait_for_unit('akkoma-initdb.service')
akkoma.systemctl('restart akkoma-initdb.service') # test repeated initialisation
akkoma.wait_for_unit('akkoma.service')
akkoma.wait_for_file('/run/akkoma/socket');
akkoma.succeed('${provisionUser}/bin/provisionUser')
akkoma.wait_for_unit('nginx.service')
client.succeed('${sendToot}/bin/sendToot')
client.succeed('${checkFe}/bin/checkFe')
'';
}
)
clientConfig =
{ pkgs, ... }:
{
environment = {
sessionVariables = {
REQUESTS_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt";
};
systemPackages = with pkgs; [ toot ];
};
};
serverConfig =
{ config, pkgs, ... }:
{
networking = {
domain = "nixos.test";
firewall.allowedTCPPorts = [ 443 ];
};
systemd.services.akkoma.confinement.enable = confined;
services.akkoma = {
enable = true;
inherit package;
config = {
":pleroma" = {
":instance" = {
name = "NixOS test Akkoma server";
description = "NixOS test Akkoma server";
email = "akkoma@nixos.test";
notify_email = "akkoma@nixos.test";
registration_open = true;
};
":media_proxy" = {
enabled = false;
};
"Pleroma.Web.Endpoint" = {
url.host = config.networking.fqdn;
};
"Pleroma.Upload" = {
base_url = "https://media.${config.networking.fqdn}/media/";
};
# disable certificate verification until we figure out how to
# supply our own certificates
":http".adapter.pools = mkRaw "%{default: [conn_opts: [transport_opts: [verify: :verify_none]]]}";
};
};
nginx.addSSL = true;
};
services.nginx.enable = true;
services.postgresql.enable = true;
};
in
{
name = "akkoma";
nodes = {
client-a =
{ ... }:
{
imports = [
clientConfig
commonConfig
];
};
client-b =
{ ... }:
{
imports = [
clientConfig
commonConfig
];
};
akkoma-a =
{ ... }:
{
imports = [
commonConfig
serverConfig
];
services.akkoma.nginx = {
sslCertificate = "${tlsCertA}/cert.pem";
sslCertificateKey = "${tlsCertA}/key.pem";
};
};
akkoma-b =
{ ... }:
{
imports = [
commonConfig
serverConfig
];
services.akkoma.nginx = {
sslCertificate = "${tlsCertB}/cert.pem";
sslCertificateKey = "${tlsCertB}/key.pem";
};
};
};
testScript = ''
import json
import random
import string
from shlex import quote
def randomString(len):
return "".join(random.choice(string.ascii_letters + string.digits) for _ in range(len))
def registerUser(user, password):
return 'pleroma_ctl user new {0} {0}@nixos.test --password {1} -y'.format(
quote(user), quote(password))
def loginUser(instance, user, password):
return 'toot login_cli -i {}.nixos.test -e {}@nixos.test -p {}'.format(
quote(instance), quote(user), quote(password))
userAName = randomString(11)
userBName = randomString(11)
userAPassword = randomString(22)
userBPassword = randomString(22)
testMessage = randomString(22)
testMedia = '${testMedia}'
start_all()
akkoma_a.wait_for_unit('akkoma-initdb.service')
akkoma_b.wait_for_unit('akkoma-initdb.service')
# test repeated initialisation
akkoma_a.systemctl('restart akkoma-initdb.service')
akkoma_a.wait_for_unit('akkoma.service')
akkoma_b.wait_for_unit('akkoma.service')
akkoma_a.wait_for_file('/run/akkoma/socket');
akkoma_b.wait_for_file('/run/akkoma/socket');
akkoma_a.succeed(registerUser(userAName, userAPassword))
akkoma_b.succeed(registerUser(userBName, userBPassword))
akkoma_a.wait_for_unit('nginx.service')
akkoma_b.wait_for_unit('nginx.service')
client_a.succeed(loginUser('akkoma-a', userAName, userAPassword))
client_b.succeed(loginUser('akkoma-b', userBName, userBPassword))
client_b.succeed('toot follow {}@akkoma-a.nixos.test'.format(userAName))
client_a.wait_until_succeeds('toot followers | grep -F -q {}'.format(quote(userBName)))
client_a.succeed('toot post {} --media {} --description "nothing to see here"'.format(
quote(testMessage), quote(testMedia)))
# verify test message
status = json.loads(client_b.wait_until_succeeds(
'toot status --json "$(toot timeline -1 | grep -E -o \'^ID [^ ]+\' | cut -d \' \' -f 2)"'))
assert status['content'] == testMessage
# compare attachment to original
client_b.succeed('cmp {} <(curl -f -S -s {})'.format(quote(testMedia),
quote(status['media_attachments'][0]['url'])))
client_a.succeed('${lib.getExe checkFe} akkoma-a.nixos.test')
client_b.succeed('${lib.getExe checkFe} akkoma-b.nixos.test')
'';
}
+3 -11
View File
@@ -1,18 +1,10 @@
# This test does a basic functionality check for alice-lg
{
system ? builtins.currentSystem,
pkgs ? import ../.. {
inherit system;
config = { };
},
pkgs,
...
}:
let
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
inherit (pkgs.lib) optionalString;
in
makeTest {
{
name = "alice-lg";
nodes = {
host1 = {
+44 -46
View File
@@ -1,48 +1,46 @@
import ./make-test-python.nix (
{ pkgs, ... }:
rec {
name = "all-terminfo";
meta = with pkgs.lib.maintainers; {
maintainers = [ jkarlson ];
{ pkgs, ... }:
{
name = "all-terminfo";
meta = with pkgs.lib.maintainers; {
maintainers = [ jkarlson ];
};
nodes.machine =
{
pkgs,
config,
lib,
...
}:
let
infoFilter =
name: drv:
let
o = builtins.tryEval drv;
in
o.success
&& lib.isDerivation o.value
&& o.value ? outputs
&& builtins.elem "terminfo" o.value.outputs
&& !o.value.meta.broken;
terminfos = lib.filterAttrs infoFilter pkgs;
excludedTerminfos = lib.filterAttrs (
_: drv: !(builtins.elem drv.terminfo config.environment.systemPackages)
) terminfos;
includedOuts = lib.filterAttrs (
_: drv: builtins.elem drv.out config.environment.systemPackages
) terminfos;
in
{
environment = {
enableAllTerminfo = true;
etc."terminfo-missing".text = builtins.concatStringsSep "\n" (builtins.attrNames excludedTerminfos);
etc."terminfo-extra-outs".text = builtins.concatStringsSep "\n" (builtins.attrNames includedOuts);
};
};
nodes.machine =
{
pkgs,
config,
lib,
...
}:
let
infoFilter =
name: drv:
let
o = builtins.tryEval drv;
in
o.success
&& lib.isDerivation o.value
&& o.value ? outputs
&& builtins.elem "terminfo" o.value.outputs
&& !o.value.meta.broken;
terminfos = lib.filterAttrs infoFilter pkgs;
excludedTerminfos = lib.filterAttrs (
_: drv: !(builtins.elem drv.terminfo config.environment.systemPackages)
) terminfos;
includedOuts = lib.filterAttrs (
_: drv: builtins.elem drv.out config.environment.systemPackages
) terminfos;
in
{
environment = {
enableAllTerminfo = true;
etc."terminfo-missing".text = builtins.concatStringsSep "\n" (builtins.attrNames excludedTerminfos);
etc."terminfo-extra-outs".text = builtins.concatStringsSep "\n" (builtins.attrNames includedOuts);
};
};
testScript = ''
machine.fail("grep . /etc/terminfo-missing >&2")
machine.fail("grep . /etc/terminfo-extra-outs >&2")
'';
}
)
testScript = ''
machine.fail("grep . /etc/terminfo-missing >&2")
machine.fail("grep . /etc/terminfo-extra-outs >&2")
'';
}
+97 -62
View File
@@ -147,48 +147,60 @@ in {
_3proxy = runTest ./3proxy.nix;
aaaaxy = runTest ./aaaaxy.nix;
acme = import ./acme/default.nix { inherit runTest; };
acme-dns = handleTest ./acme-dns.nix {};
actual = handleTest ./actual.nix {};
acme-dns = runTest ./acme-dns.nix;
actual = runTest ./actual.nix;
adguardhome = runTest ./adguardhome.nix;
aesmd = runTestOn ["x86_64-linux"] ./aesmd.nix;
agate = runTest ./web-servers/agate.nix;
agda = handleTest ./agda.nix {};
agda = runTest ./agda.nix;
age-plugin-tpm-decrypt = runTest ./age-plugin-tpm-decrypt.nix;
agorakit = runTest ./web-apps/agorakit.nix;
airsonic = handleTest ./airsonic.nix {};
akkoma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./akkoma.nix {};
akkoma-confined = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./akkoma.nix { confined = true; };
alice-lg = handleTest ./alice-lg.nix {};
alloy = handleTest ./alloy.nix {};
allTerminfo = handleTest ./all-terminfo.nix {};
alps = handleTest ./alps.nix {};
amazon-cloudwatch-agent = handleTest ./amazon-cloudwatch-agent.nix {};
amazon-init-shell = handleTest ./amazon-init-shell.nix {};
amazon-ssm-agent = handleTest ./amazon-ssm-agent.nix {};
airsonic = runTest ./airsonic.nix;
akkoma = runTestOn [ "x86_64-linux" "aarch64-linux" ] {
imports = [ ./akkoma.nix ];
_module.args.confined = false;
};
akkoma-confined = runTestOn [ "x86_64-linux" "aarch64-linux" ] {
imports = [ ./akkoma.nix ];
_module.args.confined = true;
};
alice-lg = runTest ./alice-lg.nix;
alloy = runTest ./alloy.nix;
allTerminfo = runTest ./all-terminfo.nix;
alps = runTest ./alps.nix;
amazon-cloudwatch-agent = runTest ./amazon-cloudwatch-agent.nix;
amazon-init-shell = runTest ./amazon-init-shell.nix;
amazon-ssm-agent = runTest ./amazon-ssm-agent.nix;
amd-sev = runTest ./amd-sev.nix;
angie-api = handleTest ./angie-api.nix {};
anki-sync-server = handleTest ./anki-sync-server.nix {};
anuko-time-tracker = handleTest ./anuko-time-tracker.nix {};
apcupsd = handleTest ./apcupsd.nix {};
angie-api = runTest ./angie-api.nix;
anki-sync-server = runTest ./anki-sync-server.nix;
anuko-time-tracker = runTest ./anuko-time-tracker.nix;
apcupsd = runTest ./apcupsd.nix;
apfs = runTest ./apfs.nix;
appliance-repart-image = runTest ./appliance-repart-image.nix;
appliance-repart-image-verity-store = runTest ./appliance-repart-image-verity-store.nix;
apparmor = handleTest ./apparmor {};
archi = handleTest ./archi.nix {};
aria2 = handleTest ./aria2.nix {};
armagetronad = handleTest ./armagetronad.nix {};
apparmor = runTest ./apparmor;
archi = runTest ./archi.nix;
aria2 = runTest ./aria2.nix;
armagetronad = runTest ./armagetronad.nix;
artalk = runTest ./artalk.nix;
atd = handleTest ./atd.nix {};
atd = runTest ./atd.nix;
atop = handleTest ./atop.nix {};
atticd = runTest ./atticd.nix;
atuin = runTest ./atuin.nix;
audiobookshelf = handleTest ./audiobookshelf.nix {};
auth-mysql = handleTest ./auth-mysql.nix {};
authelia = handleTest ./authelia.nix {};
auto-cpufreq = handleTest ./auto-cpufreq.nix {};
autobrr = handleTest ./autobrr.nix {};
avahi = handleTest ./avahi.nix {};
avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
audiobookshelf = runTest ./audiobookshelf.nix;
auth-mysql = runTest ./auth-mysql.nix;
authelia = runTest ./authelia.nix;
auto-cpufreq = runTest ./auto-cpufreq.nix;
autobrr = runTest ./autobrr.nix;
avahi = runTest {
imports = [ ./avahi.nix ];
_module.args.networkd = false;
};
avahi-with-resolved = runTest {
imports = [ ./avahi.nix ];
_module.args.networkd = true;
};
ayatana-indicators = runTest ./ayatana-indicators.nix;
babeld = runTest ./babeld.nix;
bazarr = handleTest ./bazarr.nix {};
@@ -416,7 +428,10 @@ in {
flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
flaresolverr = handleTest ./flaresolverr.nix {};
flood = handleTest ./flood.nix {};
floorp = handleTest ./firefox.nix { firefoxPackage = pkgs.floorp; };
floorp = runTest {
imports = [ ./firefox.nix ] ;
_module.args.firefoxPackage = pkgs.floorp;
};
fluentd = handleTest ./fluentd.nix {};
fluidd = handleTest ./fluidd.nix {};
fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {};
@@ -607,7 +622,10 @@ in {
libresprite = handleTest ./libresprite.nix {};
libreswan = runTest ./libreswan.nix;
libreswan-nat = runTest ./libreswan-nat.nix;
librewolf = handleTest ./firefox.nix { firefoxPackage = pkgs.librewolf; };
librewolf = runTest {
imports = [ ./firefox.nix ];
_module.args.firefoxPackage = pkgs.librewolf;
};
libuiohook = handleTest ./libuiohook.nix {};
libvirtd = handleTest ./libvirtd.nix {};
lidarr = handleTest ./lidarr.nix {};
@@ -616,6 +634,7 @@ in {
limesurvey = handleTest ./limesurvey.nix {};
limine = import ./limine { inherit runTest; };
listmonk = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./listmonk.nix {};
litellm = runTest ./litellm.nix;
litestream = handleTest ./litestream.nix {};
lldap = handleTest ./lldap.nix {};
localsend = handleTest ./localsend.nix {};
@@ -856,6 +875,7 @@ in {
opentelemetry-collector = handleTest ./opentelemetry-collector.nix {};
open-web-calendar = handleTest ./web-apps/open-web-calendar.nix {};
ocsinventory-agent = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./ocsinventory-agent.nix {};
orthanc = runTest ./orthanc.nix;
owncast = handleTest ./owncast.nix {};
outline = handleTest ./outline.nix {};
image-contents = handleTest ./image-contents.nix {};
@@ -896,11 +916,26 @@ in {
phosh = handleTest ./phosh.nix {};
photonvision = handleTest ./photonvision.nix {};
photoprism = handleTest ./photoprism.nix {};
php = handleTest ./php {};
php81 = handleTest ./php { php = pkgs.php81; };
php82 = handleTest ./php { php = pkgs.php82; };
php83 = handleTest ./php { php = pkgs.php83; };
php84 = handleTest ./php { php = pkgs.php84; };
php = import ./php/default.nix {
inherit runTest;
php = pkgs.php;
};
php81 = import ./php/default.nix {
inherit runTest;
php = pkgs.php81;
};
php82 = import ./php/default.nix {
inherit runTest;
php = pkgs.php82;
};
php83 = import ./php/default.nix {
inherit runTest;
php = pkgs.php83;
};
php84 = import ./php/default.nix {
inherit runTest;
php = pkgs.php84;
};
phylactery = handleTest ./web-apps/phylactery.nix {};
pict-rs = handleTest ./pict-rs.nix {};
pingvin-share = handleTest ./pingvin-share.nix {} ;
@@ -1267,26 +1302,26 @@ in {
wrappers = handleTest ./wrappers.nix {};
writefreely = handleTest ./web-apps/writefreely.nix {};
wstunnel = runTest ./wstunnel.nix;
xandikos = handleTest ./xandikos.nix {};
xautolock = handleTest ./xautolock.nix {};
xfce = handleTest ./xfce.nix {};
xfce-wayland = handleTest ./xfce-wayland.nix {};
xmonad = handleTest ./xmonad.nix {};
xmonad-xdg-autostart = handleTest ./xmonad-xdg-autostart.nix {};
xpadneo = handleTest ./xpadneo.nix {};
xrdp = handleTest ./xrdp.nix {};
xrdp-with-audio-pulseaudio = handleTest ./xrdp-with-audio-pulseaudio.nix {};
xandikos = runTest ./xandikos.nix;
xautolock = runTest ./xautolock.nix;
xfce = runTest ./xfce.nix;
xfce-wayland = runTest ./xfce-wayland.nix;
xmonad = runTest ./xmonad.nix;
xmonad-xdg-autostart = runTest ./xmonad-xdg-autostart.nix;
xpadneo = runTest ./xpadneo.nix;
xrdp = runTest ./xrdp.nix;
xrdp-with-audio-pulseaudio = runTest ./xrdp-with-audio-pulseaudio.nix;
xscreensaver = handleTest ./xscreensaver.nix {};
xss-lock = handleTest ./xss-lock.nix {};
xterm = handleTest ./xterm.nix {};
xxh = handleTest ./xxh.nix {};
yabar = handleTest ./yabar.nix {};
xss-lock = runTest ./xss-lock.nix;
xterm = runTest ./xterm.nix;
xxh = runTest ./xxh.nix;
yabar = runTest ./yabar.nix;
ydotool = handleTest ./ydotool.nix {};
yggdrasil = handleTest ./yggdrasil.nix {};
your_spotify = handleTest ./your_spotify.nix {};
zammad = handleTest ./zammad.nix {};
zenohd = handleTest ./zenohd.nix {};
zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
yggdrasil = runTest ./yggdrasil.nix;
your_spotify = runTest ./your_spotify.nix;
zammad = runTest ./zammad.nix;
zenohd = runTest ./zenohd.nix;
zeronet-conservancy = runTest ./zeronet-conservancy.nix;
zfs = handleTest ./zfs.nix {};
zigbee2mqtt_1 = runTest {
imports = [ ./zigbee2mqtt.nix ];
@@ -1296,12 +1331,12 @@ in {
imports = [ ./zigbee2mqtt.nix ];
_module.args.package = pkgs.zigbee2mqtt_2;
};
zipline = handleTest ./zipline.nix {};
zoneminder = handleTest ./zoneminder.nix {};
zookeeper = handleTest ./zookeeper.nix {};
zram-generator = handleTest ./zram-generator.nix {};
zrepl = handleTest ./zrepl.nix {};
zsh-history = handleTest ./zsh-history.nix {};
zwave-js = handleTest ./zwave-js.nix {};
zwave-js-ui = handleTest ./zwave-js-ui.nix {};
zipline = runTest ./zipline.nix;
zoneminder = runTest ./zoneminder.nix;
zookeeper = runTest ./zookeeper.nix;
zram-generator = runTest ./zram-generator.nix;
zrepl = runTest ./zrepl.nix;
zsh-history = runTest ./zsh-history.nix;
zwave-js = runTest ./zwave-js.nix;
zwave-js-ui = runTest ./zwave-js-ui.nix;
}
+28 -30
View File
@@ -1,37 +1,35 @@
import ./make-test-python.nix (
{ lib, pkgs, ... }:
{ lib, ... }:
let
nodes = {
machine = {
services.alloy = {
enable = true;
};
environment.etc."alloy/config.alloy".text = "";
let
nodes = {
machine = {
services.alloy = {
enable = true;
};
environment.etc."alloy/config.alloy".text = "";
};
in
{
name = "alloy";
};
in
{
name = "alloy";
meta = with lib.maintainers; {
maintainers = [
flokli
hbjydev
];
};
meta = with lib.maintainers; {
maintainers = [
flokli
hbjydev
];
};
inherit nodes;
inherit nodes;
testScript = ''
start_all()
testScript = ''
start_all()
machine.wait_for_unit("alloy.service")
machine.wait_for_open_port(12345)
machine.succeed(
"curl -sSfN http://127.0.0.1:12345/-/healthy"
)
machine.shutdown()
'';
}
)
machine.wait_for_unit("alloy.service")
machine.wait_for_open_port(12345)
machine.succeed(
"curl -sSfN http://127.0.0.1:12345/-/healthy"
)
machine.shutdown()
'';
}
+101 -103
View File
@@ -2,118 +2,116 @@ let
certs = import ./common/acme/server/snakeoil-certs.nix;
domain = certs.domain;
in
import ./make-test-python.nix (
{ pkgs, ... }:
{
name = "alps";
meta = with pkgs.lib.maintainers; {
maintainers = [ hmenke ];
{ pkgs, ... }:
{
name = "alps";
meta = with pkgs.lib.maintainers; {
maintainers = [ hmenke ];
};
nodes = {
server = {
imports = [ ./common/user-account.nix ];
security.pki.certificateFiles = [
certs.ca.cert
];
networking.extraHosts = ''
127.0.0.1 ${domain}
'';
networking.firewall.allowedTCPPorts = [
25
465
993
];
services.postfix = {
enable = true;
enableSubmission = true;
enableSubmissions = true;
tlsTrustedAuthorities = "${certs.ca.cert}";
sslCert = "${certs.${domain}.cert}";
sslKey = "${certs.${domain}.key}";
};
services.dovecot2 = {
enable = true;
enableImap = true;
sslCACert = "${certs.ca.cert}";
sslServerCert = "${certs.${domain}.cert}";
sslServerKey = "${certs.${domain}.key}";
};
};
nodes = {
server = {
imports = [ ./common/user-account.nix ];
client =
{ nodes, config, ... }:
{
security.pki.certificateFiles = [
certs.ca.cert
];
networking.extraHosts = ''
127.0.0.1 ${domain}
${nodes.server.config.networking.primaryIPAddress} ${domain}
'';
networking.firewall.allowedTCPPorts = [
25
465
993
];
services.postfix = {
services.alps = {
enable = true;
enableSubmission = true;
enableSubmissions = true;
tlsTrustedAuthorities = "${certs.ca.cert}";
sslCert = "${certs.${domain}.cert}";
sslKey = "${certs.${domain}.key}";
};
services.dovecot2 = {
enable = true;
enableImap = true;
sslCACert = "${certs.ca.cert}";
sslServerCert = "${certs.${domain}.cert}";
sslServerKey = "${certs.${domain}.key}";
};
};
client =
{ nodes, config, ... }:
{
security.pki.certificateFiles = [
certs.ca.cert
];
networking.extraHosts = ''
${nodes.server.config.networking.primaryIPAddress} ${domain}
'';
services.alps = {
enable = true;
theme = "alps";
imaps = {
host = domain;
port = 993;
};
smtps = {
host = domain;
port = 465;
};
theme = "alps";
imaps = {
host = domain;
port = 993;
};
smtps = {
host = domain;
port = 465;
};
environment.systemPackages = [
(pkgs.writers.writePython3Bin "test-alps-login" { } ''
from urllib.request import build_opener, HTTPCookieProcessor, Request
from urllib.parse import urlencode, urljoin
from http.cookiejar import CookieJar
baseurl = "http://localhost:${toString config.services.alps.port}"
username = "alice"
password = "${nodes.server.config.users.users.alice.password}"
cookiejar = CookieJar()
cookieprocessor = HTTPCookieProcessor(cookiejar)
opener = build_opener(cookieprocessor)
data = urlencode({"username": username, "password": password}).encode()
req = Request(urljoin(baseurl, "login"), data=data, method="POST")
with opener.open(req) as ret:
# Check that the alps_session cookie is set
print(cookiejar)
assert any(cookie.name == "alps_session" for cookie in cookiejar)
req = Request(baseurl)
with opener.open(req) as ret:
# Check that the alps_session cookie is still there...
print(cookiejar)
assert any(cookie.name == "alps_session" for cookie in cookiejar)
# ...and that we have not been redirected back to the login page
print(ret.url)
assert ret.url == urljoin(baseurl, "mailbox/INBOX")
req = Request(urljoin(baseurl, "logout"))
with opener.open(req) as ret:
# Check that the alps_session cookie is now gone
print(cookiejar)
assert all(cookie.name != "alps_session" for cookie in cookiejar)
'')
];
};
};
environment.systemPackages = [
(pkgs.writers.writePython3Bin "test-alps-login" { } ''
from urllib.request import build_opener, HTTPCookieProcessor, Request
from urllib.parse import urlencode, urljoin
from http.cookiejar import CookieJar
testScript =
{ nodes, ... }:
''
server.start()
server.wait_for_unit("postfix.service")
server.wait_for_unit("dovecot2.service")
server.wait_for_open_port(465)
server.wait_for_open_port(993)
baseurl = "http://localhost:${toString config.services.alps.port}"
username = "alice"
password = "${nodes.server.config.users.users.alice.password}"
cookiejar = CookieJar()
cookieprocessor = HTTPCookieProcessor(cookiejar)
opener = build_opener(cookieprocessor)
client.start()
client.wait_for_unit("alps.service")
client.wait_for_open_port(${toString nodes.client.config.services.alps.port})
client.succeed("test-alps-login")
'';
}
)
data = urlencode({"username": username, "password": password}).encode()
req = Request(urljoin(baseurl, "login"), data=data, method="POST")
with opener.open(req) as ret:
# Check that the alps_session cookie is set
print(cookiejar)
assert any(cookie.name == "alps_session" for cookie in cookiejar)
req = Request(baseurl)
with opener.open(req) as ret:
# Check that the alps_session cookie is still there...
print(cookiejar)
assert any(cookie.name == "alps_session" for cookie in cookiejar)
# ...and that we have not been redirected back to the login page
print(ret.url)
assert ret.url == urljoin(baseurl, "mailbox/INBOX")
req = Request(urljoin(baseurl, "logout"))
with opener.open(req) as ret:
# Check that the alps_session cookie is now gone
print(cookiejar)
assert all(cookie.name != "alps_session" for cookie in cookiejar)
'')
];
};
};
testScript =
{ nodes, ... }:
''
server.start()
server.wait_for_unit("postfix.service")
server.wait_for_unit("dovecot2.service")
server.wait_for_open_port(465)
server.wait_for_open_port(993)
client.start()
client.wait_for_unit("alps.service")
client.wait_for_open_port(${toString nodes.client.config.services.alps.port})
client.succeed("test-alps-login")
'';
}
+78 -80
View File
@@ -1,92 +1,90 @@
import ./make-test-python.nix (
{ lib, pkgs, ... }:
let
# See https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html.
iniFormat = pkgs.formats.ini { };
{ pkgs, ... }:
let
# See https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html.
iniFormat = pkgs.formats.ini { };
region = "ap-northeast-1";
sharedConfigurationDefaultProfile = "default";
sharedConfigurationFile = iniFormat.generate "config" {
"${sharedConfigurationDefaultProfile}" = {
region = region;
};
region = "ap-northeast-1";
sharedConfigurationDefaultProfile = "default";
sharedConfigurationFile = iniFormat.generate "config" {
"${sharedConfigurationDefaultProfile}" = {
region = region;
};
sharedCredentialsFile = iniFormat.generate "credentials" {
"${sharedConfigurationDefaultProfile}" = {
aws_access_key_id = "placeholder";
aws_secret_access_key = "placeholder";
aws_session_token = "placeholder";
};
};
sharedCredentialsFile = iniFormat.generate "credentials" {
"${sharedConfigurationDefaultProfile}" = {
aws_access_key_id = "placeholder";
aws_secret_access_key = "placeholder";
aws_session_token = "placeholder";
};
sharedConfigurationDirectory = pkgs.runCommand ".aws" { } ''
mkdir $out
};
sharedConfigurationDirectory = pkgs.runCommand ".aws" { } ''
mkdir $out
cp ${sharedConfigurationFile} $out/config
cp ${sharedCredentialsFile} $out/credentials
'';
in
{
name = "amazon-cloudwatch-agent";
cp ${sharedConfigurationFile} $out/config
cp ${sharedCredentialsFile} $out/credentials
'';
in
{
name = "amazon-cloudwatch-agent";
nodes.machine =
{ config, pkgs, ... }:
{
services.amazon-cloudwatch-agent = {
enable = true;
commonConfiguration = {
credentials = {
shared_credential_profile = sharedConfigurationDefaultProfile;
shared_credential_file = "${sharedConfigurationDirectory}/credentials";
};
nodes.machine =
{ config, pkgs, ... }:
{
services.amazon-cloudwatch-agent = {
enable = true;
commonConfiguration = {
credentials = {
shared_credential_profile = sharedConfigurationDefaultProfile;
shared_credential_file = "${sharedConfigurationDirectory}/credentials";
};
configuration = {
agent = {
# Required despite documentation saying the agent ignores it in "onPremise" mode.
region = region;
# Show debug logs and write to a file for interactive debugging.
debug = true;
logfile = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
};
logs = {
logs_collected = {
files = {
collect_list = [
{
file_path = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
log_group_name = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
log_stream_name = "{local_hostname}";
}
];
};
};
};
traces = {
local_mode = true;
traces_collected = {
xray = { };
};
};
};
mode = "onPremise";
};
configuration = {
agent = {
# Required despite documentation saying the agent ignores it in "onPremise" mode.
region = region;
# Keep the runtime directory for interactive debugging.
systemd.services.amazon-cloudwatch-agent.serviceConfig.RuntimeDirectoryPreserve = true;
# Show debug logs and write to a file for interactive debugging.
debug = true;
logfile = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
};
logs = {
logs_collected = {
files = {
collect_list = [
{
file_path = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
log_group_name = "/var/log/amazon-cloudwatch-agent/amazon-cloudwatch-agent.log";
log_stream_name = "{local_hostname}";
}
];
};
};
};
traces = {
local_mode = true;
traces_collected = {
xray = { };
};
};
};
mode = "onPremise";
};
testScript = ''
start_all()
# Keep the runtime directory for interactive debugging.
systemd.services.amazon-cloudwatch-agent.serviceConfig.RuntimeDirectoryPreserve = true;
};
machine.wait_for_unit("amazon-cloudwatch-agent.service")
testScript = ''
start_all()
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.pid")
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.toml")
# "config-translator" omits this file if no trace configurations are specified.
#
# See https://github.com/aws/amazon-cloudwatch-agent/issues/1320.
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.yaml")
machine.wait_for_file("/run/amazon-cloudwatch-agent/env-config.json")
'';
}
)
machine.wait_for_unit("amazon-cloudwatch-agent.service")
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.pid")
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.toml")
# "config-translator" omits this file if no trace configurations are specified.
#
# See https://github.com/aws/amazon-cloudwatch-agent/issues/1320.
machine.wait_for_file("/run/amazon-cloudwatch-agent/amazon-cloudwatch-agent.yaml")
machine.wait_for_file("/run/amazon-cloudwatch-agent/env-config.json")
'';
}
+21 -27
View File
@@ -6,41 +6,35 @@
# configuration expression.
{
system ? builtins.currentSystem,
config ? { },
pkgs ? import ../.. { inherit system config; },
lib,
...
}:
with import ../lib/testing-python.nix { inherit system pkgs; };
with pkgs.lib;
makeTest {
{
name = "amazon-init";
meta = with maintainers; {
meta = with lib.maintainers; {
maintainers = [ urbas ];
};
nodes.machine =
{ lib, pkgs, ... }:
{
imports = [
../modules/profiles/headless.nix
../modules/virtualisation/amazon-init.nix
];
services.openssh.enable = true;
system.switch.enable = true;
networking.hostName = "";
environment.etc."ec2-metadata/user-data" = {
text = ''
#!/usr/bin/bash
nodes.machine = {
imports = [
../modules/profiles/headless.nix
../modules/virtualisation/amazon-init.nix
];
services.openssh.enable = true;
system.switch.enable = true;
networking.hostName = "";
environment.etc."ec2-metadata/user-data" = {
text = ''
#!/usr/bin/bash
echo successful > /tmp/evidence
echo successful > /tmp/evidence
# Emulate running nixos-rebuild switch, just without any building.
# https://github.com/nixos/nixpkgs/blob/4c62505847d88f16df11eff3c81bf9a453a4979e/nixos/modules/virtualisation/amazon-init.nix#L55
/run/current-system/bin/switch-to-configuration test
'';
};
# Emulate running nixos-rebuild switch, just without any building.
# https://github.com/nixos/nixpkgs/blob/4c62505847d88f16df11eff3c81bf9a453a4979e/nixos/modules/virtualisation/amazon-init.nix#L55
/run/current-system/bin/switch-to-configuration test
'';
};
};
testScript = ''
# To wait until amazon-init terminates its run
unnamed.wait_for_unit("amazon-init.service")
+14 -18
View File
@@ -1,22 +1,18 @@
import ./make-test-python.nix (
{ lib, pkgs, ... }:
{
name = "amazon-ssm-agent";
meta.maintainers = [ lib.maintainers.anthonyroussel ];
{ lib, ... }:
{
name = "amazon-ssm-agent";
meta.maintainers = [ lib.maintainers.anthonyroussel ];
nodes.machine =
{ config, pkgs, ... }:
{
services.amazon-ssm-agent.enable = true;
};
nodes.machine = {
services.amazon-ssm-agent.enable = true;
};
testScript = ''
start_all()
testScript = ''
start_all()
machine.wait_for_file("/etc/amazon/ssm/seelog.xml")
machine.wait_for_file("/etc/amazon/ssm/amazon-ssm-agent.json")
machine.wait_for_file("/etc/amazon/ssm/seelog.xml")
machine.wait_for_file("/etc/amazon/ssm/amazon-ssm-agent.json")
machine.wait_for_unit("amazon-ssm-agent.service")
'';
}
)
machine.wait_for_unit("amazon-ssm-agent.service")
'';
}
+143 -145
View File
@@ -1,170 +1,168 @@
import ./make-test-python.nix (
{ lib, pkgs, ... }:
let
hosts = ''
192.168.2.101 example.com
192.168.2.101 api.example.com
192.168.2.101 backend.example.com
'';
{ lib, pkgs, ... }:
let
hosts = ''
192.168.2.101 example.com
192.168.2.101 api.example.com
192.168.2.101 backend.example.com
'';
in
{
name = "angie-api";
meta.maintainers = with pkgs.lib.maintainers; [ izorkin ];
in
{
name = "angie-api";
meta.maintainers = with pkgs.lib.maintainers; [ izorkin ];
nodes = {
server =
{ pkgs, ... }:
{
networking = {
interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.168.2.101";
prefixLength = 24;
}
];
nodes = {
server =
{ pkgs, ... }:
{
networking = {
interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.168.2.101";
prefixLength = 24;
}
];
};
extraHosts = hosts;
firewall.allowedTCPPorts = [ 80 ];
};
services.nginx = {
enable = true;
package = pkgs.angie;
upstreams = {
"backend-http" = {
servers = {
"backend.example.com:8080" = {
fail_timeout = "0";
};
};
extraConfig = ''
zone upstream 256k;
'';
};
"backend-socket" = {
servers = {
"unix:/run/example.sock" = {
fail_timeout = "0";
};
};
extraConfig = ''
zone upstream 256k;
'';
};
extraHosts = hosts;
firewall.allowedTCPPorts = [ 80 ];
};
services.nginx = {
enable = true;
package = pkgs.angie;
virtualHosts."api.example.com" = {
locations."/console/" = {
extraConfig = ''
api /status/;
upstreams = {
"backend-http" = {
servers = {
"backend.example.com:8080" = {
fail_timeout = "0";
};
};
extraConfig = ''
zone upstream 256k;
'';
};
"backend-socket" = {
servers = {
"unix:/run/example.sock" = {
fail_timeout = "0";
};
};
extraConfig = ''
zone upstream 256k;
'';
};
allow 192.168.2.201;
deny all;
'';
};
};
virtualHosts."api.example.com" = {
locations."/console/" = {
extraConfig = ''
api /status/;
virtualHosts."example.com" = {
locations."/test/" = {
root = lib.mkForce (
pkgs.runCommandLocal "testdir" { } ''
mkdir -p "$out/test"
cat > "$out/test/index.html" <<EOF
<html><body>Hello World!</body></html>
EOF
''
);
extraConfig = ''
status_zone test_zone;
allow 192.168.2.201;
deny all;
'';
};
allow 192.168.2.201;
deny all;
'';
};
locations."/test/locked/" = {
extraConfig = ''
status_zone test_zone;
virtualHosts."example.com" = {
locations."/test/" = {
root = lib.mkForce (
pkgs.runCommandLocal "testdir" { } ''
mkdir -p "$out/test"
cat > "$out/test/index.html" <<EOF
<html><body>Hello World!</body></html>
EOF
''
);
extraConfig = ''
status_zone test_zone;
deny all;
'';
};
locations."/test/error/" = {
extraConfig = ''
status_zone test_zone;
allow 192.168.2.201;
deny all;
'';
};
locations."/test/locked/" = {
extraConfig = ''
status_zone test_zone;
deny all;
'';
};
locations."/test/error/" = {
extraConfig = ''
status_zone test_zone;
allow all;
'';
};
locations."/upstream-http/" = {
proxyPass = "http://backend-http";
};
locations."/upstream-socket/" = {
proxyPass = "http://backend-socket";
};
allow all;
'';
};
locations."/upstream-http/" = {
proxyPass = "http://backend-http";
};
locations."/upstream-socket/" = {
proxyPass = "http://backend-socket";
};
};
};
};
client =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.jq ];
networking = {
interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.168.2.201";
prefixLength = 24;
}
];
};
extraHosts = hosts;
client =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.jq ];
networking = {
interfaces.eth1 = {
ipv4.addresses = [
{
address = "192.168.2.201";
prefixLength = 24;
}
];
};
extraHosts = hosts;
};
};
};
};
testScript = ''
start_all()
testScript = ''
start_all()
server.wait_for_unit("nginx")
server.wait_for_open_port(80)
server.wait_for_unit("nginx")
server.wait_for_open_port(80)
# Check Angie version
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.angie.version' | grep '${pkgs.angie.version}'")
# Check Angie version
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.angie.version' | grep '${pkgs.angie.version}'")
# Check access
client.succeed("curl --verbose --head http://api.example.com/console/ | grep 'HTTP/1.1 200'")
server.succeed("curl --verbose --head http://api.example.com/console/ | grep 'HTTP/1.1 403 Forbidden'")
# Check access
client.succeed("curl --verbose --head http://api.example.com/console/ | grep 'HTTP/1.1 200'")
server.succeed("curl --verbose --head http://api.example.com/console/ | grep 'HTTP/1.1 403 Forbidden'")
# Check responses and requests
client.succeed("curl --verbose http://example.com/test/")
client.succeed("curl --verbose http://example.com/test/locked/")
client.succeed("curl --verbose http://example.com/test/locked/")
client.succeed("curl --verbose http://example.com/test/error/")
client.succeed("curl --verbose http://example.com/test/error/")
client.succeed("curl --verbose http://example.com/test/error/")
server.succeed("curl --verbose http://example.com/test/")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"200\"' | grep '1'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"403\"' | grep '3'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"404\"' | grep '3'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.requests.total' | grep '7'")
# Check responses and requests
client.succeed("curl --verbose http://example.com/test/")
client.succeed("curl --verbose http://example.com/test/locked/")
client.succeed("curl --verbose http://example.com/test/locked/")
client.succeed("curl --verbose http://example.com/test/error/")
client.succeed("curl --verbose http://example.com/test/error/")
client.succeed("curl --verbose http://example.com/test/error/")
server.succeed("curl --verbose http://example.com/test/")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"200\"' | grep '1'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"403\"' | grep '3'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.responses.\"404\"' | grep '3'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.location_zones.test_zone.requests.total' | grep '7'")
# Check upstreams
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".state' | grep 'up'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".health.fails' | grep '0'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".state' | grep 'up'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".health.fails' | grep '0'")
client.succeed("curl --verbose http://example.com/upstream-http/")
client.succeed("curl --verbose http://example.com/upstream-socket/")
client.succeed("curl --verbose http://example.com/upstream-socket/")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".health.fails' | grep '1'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".health.fails' | grep '2'")
# Check upstreams
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".state' | grep 'up'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".health.fails' | grep '0'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".state' | grep 'up'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".health.fails' | grep '0'")
client.succeed("curl --verbose http://example.com/upstream-http/")
client.succeed("curl --verbose http://example.com/upstream-socket/")
client.succeed("curl --verbose http://example.com/upstream-socket/")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-http\".peers.\"192.168.2.101:8080\".health.fails' | grep '1'")
client.succeed("curl --verbose http://api.example.com/console/ | jq -e '.http.upstreams.\"backend-socket\".peers.\"unix:/run/example.sock\".health.fails' | grep '2'")
server.shutdown()
client.shutdown()
'';
}
)
server.shutdown()
client.shutdown()
'';
}
+61 -65
View File
@@ -1,75 +1,71 @@
import ./make-test-python.nix (
{ pkgs, ... }:
let
ankiSyncTest = pkgs.writeScript "anki-sync-test.py" ''
#!${pkgs.python3}/bin/python
{ pkgs, ... }:
let
ankiSyncTest = pkgs.writeScript "anki-sync-test.py" ''
#!${pkgs.python3}/bin/python
import sys
import sys
# get site paths from anki itself
from runpy import run_path
run_path("${pkgs.anki}/bin/.anki-wrapped")
import anki
# get site paths from anki itself
from runpy import run_path
run_path("${pkgs.anki}/bin/.anki-wrapped")
import anki
col = anki.collection.Collection('test_collection')
endpoint = 'http://localhost:27701'
col = anki.collection.Collection('test_collection')
endpoint = 'http://localhost:27701'
# Sanity check: verify bad login fails
try:
col.sync_login('baduser', 'badpass', endpoint)
print("bad user login worked?!")
sys.exit(1)
except anki.errors.SyncError:
pass
# Sanity check: verify bad login fails
try:
col.sync_login('baduser', 'badpass', endpoint)
print("bad user login worked?!")
sys.exit(1)
except anki.errors.SyncError:
pass
# test logging in to users
col.sync_login('user', 'password', endpoint)
col.sync_login('passfileuser', 'passfilepassword', endpoint)
# test logging in to users
col.sync_login('user', 'password', endpoint)
col.sync_login('passfileuser', 'passfilepassword', endpoint)
# Test actual sync. login apparently doesn't remember the endpoint...
login = col.sync_login('user', 'password', endpoint)
login.endpoint = endpoint
sync = col.sync_collection(login, False)
assert sync.required == sync.NO_CHANGES
# TODO: create an archive with server content including a test card
# and check we got it?
'';
testPasswordFile = pkgs.writeText "anki-password" "passfilepassword";
in
{
name = "anki-sync-server";
meta = with pkgs.lib.maintainers; {
maintainers = [ martinetd ];
# Test actual sync. login apparently doesn't remember the endpoint...
login = col.sync_login('user', 'password', endpoint)
login.endpoint = endpoint
sync = col.sync_collection(login, False)
assert sync.required == sync.NO_CHANGES
# TODO: create an archive with server content including a test card
# and check we got it?
'';
testPasswordFile = pkgs.writeText "anki-password" "passfilepassword";
in
{
name = "anki-sync-server";
meta = with pkgs.lib.maintainers; {
maintainers = [ martinetd ];
};
nodes.machine = {
services.anki-sync-server = {
enable = true;
users = [
{
username = "user";
password = "password";
}
{
username = "passfileuser";
passwordFile = testPasswordFile;
}
];
};
};
nodes.machine =
{ pkgs, ... }:
{
services.anki-sync-server = {
enable = true;
users = [
{
username = "user";
password = "password";
}
{
username = "passfileuser";
passwordFile = testPasswordFile;
}
];
};
};
testScript = ''
start_all()
testScript = ''
start_all()
with subtest("Server starts successfully"):
# service won't start without users
machine.wait_for_unit("anki-sync-server.service")
machine.wait_for_open_port(27701)
with subtest("Server starts successfully"):
# service won't start without users
machine.wait_for_unit("anki-sync-server.service")
machine.wait_for_open_port(27701)
with subtest("Can sync"):
machine.succeed("${ankiSyncTest}")
'';
}
)
with subtest("Can sync"):
machine.succeed("${ankiSyncTest}")
'';
}
+17 -19
View File
@@ -1,20 +1,18 @@
import ./make-test-python.nix (
{ pkgs, ... }:
{
name = "anuko-time-tracker";
meta = {
maintainers = with pkgs.lib.maintainers; [ michaelshmitty ];
{ pkgs, ... }:
{
name = "anuko-time-tracker";
meta = {
maintainers = with pkgs.lib.maintainers; [ michaelshmitty ];
};
nodes = {
machine = {
services.anuko-time-tracker.enable = true;
};
nodes = {
machine = {
services.anuko-time-tracker.enable = true;
};
};
testScript = ''
start_all()
machine.wait_for_unit("phpfpm-anuko-time-tracker")
machine.wait_for_open_port(80);
machine.wait_until_succeeds("curl -s --fail -L http://localhost/time.php | grep 'Anuko Time Tracker'")
'';
}
)
};
testScript = ''
start_all()
machine.wait_for_unit("phpfpm-anuko-time-tracker")
machine.wait_for_open_port(80);
machine.wait_until_succeeds("curl -s --fail -L http://localhost/time.php | grep 'Anuko Time Tracker'")
'';
}
+36 -38
View File
@@ -2,45 +2,43 @@ let
# arbitrary address
ipAddr = "192.168.42.42";
in
import ./make-test-python.nix (
{ lib, pkgs, ... }:
{
name = "apcupsd";
meta.maintainers = with lib.maintainers; [ bjornfor ];
{ lib, ... }:
{
name = "apcupsd";
meta.maintainers = with lib.maintainers; [ bjornfor ];
nodes = {
machine = {
services.apcupsd = {
enable = true;
configText = ''
UPSTYPE usb
BATTERYLEVEL 42
# Configure NISIP so that the only way apcaccess can work is to read
# this config.
NISIP ${ipAddr}
'';
};
networking.interfaces.eth1 = {
ipv4.addresses = [
{
address = ipAddr;
prefixLength = 24;
}
];
};
nodes = {
machine = {
services.apcupsd = {
enable = true;
configText = ''
UPSTYPE usb
BATTERYLEVEL 42
# Configure NISIP so that the only way apcaccess can work is to read
# this config.
NISIP ${ipAddr}
'';
};
networking.interfaces.eth1 = {
ipv4.addresses = [
{
address = ipAddr;
prefixLength = 24;
}
];
};
};
};
# Check that the service starts, that the CLI (apcaccess) works and that it
# uses the config (ipAddr) defined in the service config.
testScript = ''
start_all()
machine.wait_for_unit("apcupsd.service")
machine.wait_for_open_port(3551, "${ipAddr}")
res = machine.succeed("apcaccess")
expect_line="MBATTCHG : 42 Percent"
assert "MBATTCHG : 42 Percent" in res, f"expected apcaccess output to contain '{expect_line}' but got '{res}'"
machine.shutdown()
'';
}
)
# Check that the service starts, that the CLI (apcaccess) works and that it
# uses the config (ipAddr) defined in the service config.
testScript = ''
start_all()
machine.wait_for_unit("apcupsd.service")
machine.wait_for_open_port(3551, "${ipAddr}")
res = machine.succeed("apcaccess")
expect_line="MBATTCHG : 42 Percent"
assert "MBATTCHG : 42 Percent" in res, f"expected apcaccess output to contain '{expect_line}' but got '{res}'"
machine.shutdown()
'';
}
+111 -115
View File
@@ -1,130 +1,126 @@
import ../make-test-python.nix (
{ pkgs, lib, ... }:
let
helloProfileContents = ''
abi <abi/4.0>,
include <tunables/global>
profile hello ${lib.getExe pkgs.hello} {
include <abstractions/base>
}
'';
in
{
name = "apparmor";
meta.maintainers = with lib.maintainers; [
julm
grimmauld
];
{ pkgs, lib, ... }:
let
helloProfileContents = ''
abi <abi/4.0>,
include <tunables/global>
profile hello ${lib.getExe pkgs.hello} {
include <abstractions/base>
}
'';
in
{
name = "apparmor";
meta.maintainers = with lib.maintainers; [
julm
grimmauld
];
nodes.machine =
{
lib,
pkgs,
config,
...
}:
{
security.apparmor = {
enable = lib.mkDefault true;
nodes.machine =
{
lib,
...
}:
{
security.apparmor = {
enable = lib.mkDefault true;
policies.hello = {
# test profile enforce and content definition
state = "enforce";
profile = helloProfileContents;
};
policies.hello = {
# test profile enforce and content definition
state = "enforce";
profile = helloProfileContents;
};
policies.sl = {
# test profile complain and path definition
state = "complain";
path = ./sl_profile;
};
policies.sl = {
# test profile complain and path definition
state = "complain";
path = ./sl_profile;
};
policies.hexdump = {
# test profile complain and path definition
state = "enforce";
profile = ''
abi <abi/4.0>,
include <tunables/global>
profile hexdump /nix/store/*/bin/hexdump {
include <abstractions/base>
deny /tmp/** r,
}
'';
};
includes."abstractions/base" = ''
/nix/store/*/bin/** mr,
/nix/store/*/lib/** mr,
/nix/store/** r,
policies.hexdump = {
# test profile complain and path definition
state = "enforce";
profile = ''
abi <abi/4.0>,
include <tunables/global>
profile hexdump /nix/store/*/bin/hexdump {
include <abstractions/base>
deny /tmp/** r,
}
'';
};
includes."abstractions/base" = ''
/nix/store/*/bin/** mr,
/nix/store/*/lib/** mr,
/nix/store/** r,
'';
};
};
testScript =
let
inherit (lib) getExe getExe';
in
''
machine.wait_for_unit("multi-user.target")
testScript =
let
inherit (lib) getExe getExe';
in
''
machine.wait_for_unit("multi-user.target")
with subtest("AppArmor profiles are loaded"):
machine.succeed("systemctl status apparmor.service")
with subtest("AppArmor profiles are loaded"):
machine.succeed("systemctl status apparmor.service")
# AppArmor securityfs
with subtest("AppArmor securityfs is mounted"):
machine.succeed("mountpoint -q /sys/kernel/security")
machine.succeed("cat /sys/kernel/security/apparmor/profiles")
# AppArmor securityfs
with subtest("AppArmor securityfs is mounted"):
machine.succeed("mountpoint -q /sys/kernel/security")
machine.succeed("cat /sys/kernel/security/apparmor/profiles")
# Test apparmorRulesFromClosure by:
# 1. Prepending a string of the relevant packages' name and version on each line.
# 2. Sorting according to those strings.
# 3. Removing those prepended strings.
# 4. Using `diff` against the expected output.
with subtest("apparmorRulesFromClosure"):
machine.succeed(
"${getExe' pkgs.diffutils "diff"} -u ${
pkgs.writeText "expected.rules" (import ./makeExpectedPolicies.nix { inherit pkgs; })
} ${
pkgs.runCommand "actual.rules" { preferLocalBuild = true; } ''
${getExe pkgs.gnused} -e 's:^[^ ]* ${builtins.storeDir}/[^,/-]*-\([^/,]*\):\1 \0:' ${
pkgs.apparmorRulesFromClosure {
name = "ping";
additionalRules = [ "x $path/foo/**" ];
} [ pkgs.libcap ]
} |
${getExe' pkgs.coreutils "sort"} -n -k1 |
${getExe pkgs.gnused} -e 's:^[^ ]* ::' >$out
''
}"
)
# Test apparmorRulesFromClosure by:
# 1. Prepending a string of the relevant packages' name and version on each line.
# 2. Sorting according to those strings.
# 3. Removing those prepended strings.
# 4. Using `diff` against the expected output.
with subtest("apparmorRulesFromClosure"):
machine.succeed(
"${getExe' pkgs.diffutils "diff"} -u ${
pkgs.writeText "expected.rules" (import ./makeExpectedPolicies.nix { inherit pkgs; })
} ${
pkgs.runCommand "actual.rules" { preferLocalBuild = true; } ''
${getExe pkgs.gnused} -e 's:^[^ ]* ${builtins.storeDir}/[^,/-]*-\([^/,]*\):\1 \0:' ${
pkgs.apparmorRulesFromClosure {
name = "ping";
additionalRules = [ "x $path/foo/**" ];
} [ pkgs.libcap ]
} |
${getExe' pkgs.coreutils "sort"} -n -k1 |
${getExe pkgs.gnused} -e 's:^[^ ]* ::' >$out
''
}"
)
# Test apparmor profile states by using `diff` against `aa-status`
with subtest("apparmorProfileStates"):
machine.succeed("${getExe' pkgs.diffutils "diff"} -u \
<(${getExe' pkgs.apparmor-bin-utils "aa-status"} --json | ${getExe pkgs.jq} --sort-keys . ) \
<(${getExe pkgs.jq} --sort-keys . ${
pkgs.writers.writeJSON "expectedStates.json" {
version = "2";
processes = { };
profiles = {
hexdump = "enforce";
hello = "enforce";
sl = "complain";
};
}
})")
# Test apparmor profile states by using `diff` against `aa-status`
with subtest("apparmorProfileStates"):
machine.succeed("${getExe' pkgs.diffutils "diff"} -u \
<(${getExe' pkgs.apparmor-bin-utils "aa-status"} --json | ${getExe pkgs.jq} --sort-keys . ) \
<(${getExe pkgs.jq} --sort-keys . ${
pkgs.writers.writeJSON "expectedStates.json" {
version = "2";
processes = { };
profiles = {
hexdump = "enforce";
hello = "enforce";
sl = "complain";
};
}
})")
# Test apparmor profile files in /etc/apparmor.d/<name> to be either a correct symlink (sl) or have the right file contents (hello)
with subtest("apparmorProfileTargets"):
machine.succeed("${getExe' pkgs.diffutils "diff"} -u <(${getExe pkgs.file} /etc/static/apparmor.d/sl) ${pkgs.writeText "expected.link" ''
/etc/static/apparmor.d/sl: symbolic link to ${./sl_profile}
''}")
machine.succeed("${getExe' pkgs.diffutils "diff"} -u /etc/static/apparmor.d/hello ${pkgs.writeText "expected.content" helloProfileContents}")
# Test apparmor profile files in /etc/apparmor.d/<name> to be either a correct symlink (sl) or have the right file contents (hello)
with subtest("apparmorProfileTargets"):
machine.succeed("${getExe' pkgs.diffutils "diff"} -u <(${getExe pkgs.file} /etc/static/apparmor.d/sl) ${pkgs.writeText "expected.link" ''
/etc/static/apparmor.d/sl: symbolic link to ${./sl_profile}
''}")
machine.succeed("${getExe' pkgs.diffutils "diff"} -u /etc/static/apparmor.d/hello ${pkgs.writeText "expected.content" helloProfileContents}")
with subtest("apparmorProfileEnforce"):
machine.succeed("${getExe pkgs.hello} 1> /tmp/test-file")
machine.fail("${lib.getExe' pkgs.util-linux "hexdump"} /tmp/test-file") # no access to /tmp/test-file granted by apparmor
'';
}
)
with subtest("apparmorProfileEnforce"):
machine.succeed("${getExe pkgs.hello} 1> /tmp/test-file")
machine.fail("${lib.getExe' pkgs.util-linux "hexdump"} /tmp/test-file") # no access to /tmp/test-file granted by apparmor
'';
}
+18 -9
View File
@@ -1,14 +1,14 @@
{ pkgs }:
''
ixr ${pkgs.bash}/libexec/**,
mr ${pkgs.bash}/lib/**.so*,
mr ${pkgs.bash}/lib64/**.so*,
mr ${pkgs.bash}/share/**,
r ${pkgs.bash},
r ${pkgs.bash}/etc/**,
r ${pkgs.bash}/lib/**,
r ${pkgs.bash}/lib64/**,
x ${pkgs.bash}/foo/**,
ixr ${pkgs.bashNonInteractive}/libexec/**,
mr ${pkgs.bashNonInteractive}/lib/**.so*,
mr ${pkgs.bashNonInteractive}/lib64/**.so*,
mr ${pkgs.bashNonInteractive}/share/**,
r ${pkgs.bashNonInteractive},
r ${pkgs.bashNonInteractive}/etc/**,
r ${pkgs.bashNonInteractive}/lib/**,
r ${pkgs.bashNonInteractive}/lib64/**,
x ${pkgs.bashNonInteractive}/foo/**,
ixr ${pkgs.glibc}/libexec/**,
mr ${pkgs.glibc}/lib/**.so*,
mr ${pkgs.glibc}/lib64/**.so*,
@@ -54,6 +54,15 @@
r ${pkgs.libunistring}/lib/**,
r ${pkgs.libunistring}/lib64/**,
x ${pkgs.libunistring}/foo/**,
ixr ${pkgs.tzdata}/libexec/**,
mr ${pkgs.tzdata}/lib/**.so*,
mr ${pkgs.tzdata}/lib64/**.so*,
mr ${pkgs.tzdata}/share/**,
r ${pkgs.tzdata},
r ${pkgs.tzdata}/etc/**,
r ${pkgs.tzdata}/lib/**,
r ${pkgs.tzdata}/lib64/**,
x ${pkgs.tzdata}/foo/**,
ixr ${pkgs.glibc.libgcc}/libexec/**,
mr ${pkgs.glibc.libgcc}/lib/**.so*,
mr ${pkgs.glibc.libgcc}/lib64/**.so*,
+28 -30
View File
@@ -1,38 +1,36 @@
import ./make-test-python.nix (
{ lib, ... }:
{
name = "archi";
meta.maintainers = with lib.maintainers; [ paumr ];
{ lib, ... }:
{
name = "archi";
meta.maintainers = with lib.maintainers; [ paumr ];
nodes.machine =
{ pkgs, ... }:
{
imports = [
./common/x11.nix
];
nodes.machine =
{ pkgs, ... }:
{
imports = [
./common/x11.nix
];
environment.systemPackages = with pkgs; [ archi ];
};
environment.systemPackages = with pkgs; [ archi ];
};
enableOCR = true;
enableOCR = true;
testScript = ''
machine.wait_for_x()
testScript = ''
machine.wait_for_x()
with subtest("createEmptyModel via CLI"):
machine.succeed("Archi -application com.archimatetool.commandline.app -consoleLog -nosplash --createEmptyModel --saveModel smoke.archimate")
machine.copy_from_vm("smoke.archimate", "")
with subtest("createEmptyModel via CLI"):
machine.succeed("Archi -application com.archimatetool.commandline.app -consoleLog -nosplash --createEmptyModel --saveModel smoke.archimate")
machine.copy_from_vm("smoke.archimate", "")
with subtest("UI smoketest"):
machine.succeed("DISPLAY=:0 Archi --createEmptyModel >&2 &")
machine.wait_for_window("Archi")
with subtest("UI smoketest"):
machine.succeed("DISPLAY=:0 Archi --createEmptyModel >&2 &")
machine.wait_for_window("Archi")
# wait till main UI is open
# since OCR seems to be buggy wait_for_text was replaced by sleep, issue: #302965
# machine.wait_for_text("Welcome to Archi")
machine.sleep(20)
# wait till main UI is open
# since OCR seems to be buggy wait_for_text was replaced by sleep, issue: #302965
# machine.wait_for_text("Welcome to Archi")
machine.sleep(20)
machine.screenshot("welcome-screen")
'';
}
)
machine.screenshot("welcome-screen")
'';
}
+47 -49
View File
@@ -1,54 +1,52 @@
import ./make-test-python.nix (
{ pkgs, ... }:
let
rpcSecret = "supersecret";
rpc-listen-port = 6800;
curlBody = {
jsonrpc = 2.0;
id = 1;
method = "aria2.getVersion";
params = [ "token:${rpcSecret}" ];
};
in
rec {
name = "aria2";
{ pkgs, ... }:
let
rpcSecret = "supersecret";
rpc-listen-port = 6800;
curlBody = {
jsonrpc = 2.0;
id = 1;
method = "aria2.getVersion";
params = [ "token:${rpcSecret}" ];
};
in
{
name = "aria2";
nodes.machine = {
environment.etc."aria2Rpc".text = rpcSecret;
services.aria2 = {
enable = true;
rpcSecretFile = "/etc/aria2Rpc";
settings = {
inherit rpc-listen-port;
allow-overwrite = false;
check-integrity = true;
console-log-level = "warn";
listen-port = [
{
from = 20000;
to = 20010;
}
{
from = 22222;
to = 22222;
}
];
max-concurrent-downloads = 50;
seed-ratio = 1.2;
summary-interval = 0;
};
nodes.machine = {
environment.etc."aria2Rpc".text = rpcSecret;
services.aria2 = {
enable = true;
rpcSecretFile = "/etc/aria2Rpc";
settings = {
inherit rpc-listen-port;
allow-overwrite = false;
check-integrity = true;
console-log-level = "warn";
listen-port = [
{
from = 20000;
to = 20010;
}
{
from = 22222;
to = 22222;
}
];
max-concurrent-downloads = 50;
seed-ratio = 1.2;
summary-interval = 0;
};
};
};
testScript = ''
machine.start()
machine.wait_for_unit("aria2.service")
curl_cmd = 'curl --fail-with-body -X POST -H "Content-Type: application/json" \
-d \'${builtins.toJSON curlBody}\' http://localhost:${toString rpc-listen-port}/jsonrpc'
print(machine.wait_until_succeeds(curl_cmd, timeout=10))
machine.shutdown()
'';
testScript = ''
machine.start()
machine.wait_for_unit("aria2.service")
curl_cmd = 'curl --fail-with-body -X POST -H "Content-Type: application/json" \
-d \'${builtins.toJSON curlBody}\' http://localhost:${toString rpc-listen-port}/jsonrpc'
print(machine.wait_until_succeeds(curl_cmd, timeout=10))
machine.shutdown()
'';
meta.maintainers = [ pkgs.lib.maintainers.timhae ];
}
)
meta.maintainers = [ pkgs.lib.maintainers.timhae ];
}

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