From 56651f1c51fc6c24dfdc654064e860ed71cbe0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 10:45:27 +0200 Subject: [PATCH 01/19] fix: add rustfs --- doc/rustfs.md | 45 +++++++++++++++ doc/services.md | 1 + nix/services/default.nix | 1 + nix/services/rustfs.nix | 105 +++++++++++++++++++++++++++++++++++ nix/services/rustfs_test.nix | 27 +++++++++ test/flake.lock | 57 +++++++++++++++++++ test/flake.nix | 3 + test/nix/pkgs.nix | 46 +++++++++------ 8 files changed, 269 insertions(+), 16 deletions(-) create mode 100644 doc/rustfs.md create mode 100644 nix/services/rustfs.nix create mode 100644 nix/services/rustfs_test.nix diff --git a/doc/rustfs.md b/doc/rustfs.md new file mode 100644 index 00000000..1199bbfb --- /dev/null +++ b/doc/rustfs.md @@ -0,0 +1,45 @@ +# RustFS + +[Qdrant](https://github.com/qdrant/qdrant) is a vector similarity search engine and database for AI applications. + +Authentik is not in nixpkgs. You must provide the packages yourself via [`components`](#components) from the [`authentik-nix`](https://github.com/nix-community/authentik-nix) flake input. +It also needs a PostgreSQL and a Redis instance. + +## Getting Started + +```nix +perSystem = {config, inputs', ...}: +let + ak = config.process-compose."authentik".services.authentik.ak; +in +{ + process-compose."authentik" = { + # Configure the sidecar postgres and a redis processes. + services.postgres = ak.services.postgres; + services.redis = ak.services.redis; + + # Configure authentik. + services.authentik."authentik" = { + enable = true; + + compponents = inputs.authentik-nix.packages.${system}; + secretKey = "dev-secret"; + + settings = { + listen.http = "0.0.0.0:9000"; + + postgresql = { + host = "127.0.0.1"; + port = 5433; + user = "authentik"; + name = "authentik"; + password = "authentik"; + }; + + redis.host = "127.0.0.1"; + redis.port = 6378; + }; + }; + }; +` +``` diff --git a/doc/services.md b/doc/services.md index 00a499bf..564a4d7d 100644 --- a/doc/services.md +++ b/doc/services.md @@ -34,6 +34,7 @@ short-title: Services - [[qdrant]]# - [[redis]]# - [[redis-cluster]] +- [[rustfs]]# - [[seaweedfs]]# - [[searxng]]# - [[tika]]# diff --git a/nix/services/default.nix b/nix/services/default.nix index 58c82bc9..63c531d0 100644 --- a/nix/services/default.nix +++ b/nix/services/default.nix @@ -38,6 +38,7 @@ in ./qdrant.nix ./chromadb.nix ./neo4j.nix + ./rustfs.nix ]) ++ [ ./devshell.nix ]; diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix new file mode 100644 index 00000000..2aa030e3 --- /dev/null +++ b/nix/services/rustfs.nix @@ -0,0 +1,105 @@ +{ config +, lib +, name +, ... +}: + +let + inherit (lib) types; +in +{ + options = { + package = lib.mkOption { + type = types.package; + description = "Which package of RustFS to use"; + defaultText = lib.literalExpression "inputs.rustfs.packages.\${pkgs.stdenv.hostPlatform.system}.default"; + }; + + server = { + host = lib.mkOption { + type = types.nullOr types.str; + default = "127.0.0.1"; + description = '' + The IP interface to bind to. + `null` means "all interfaces". + ''; + }; + + port = lib.mkOption { + type = types.port; + default = 9000; + description = "The TCP port for the S3 API."; + }; + }; + + console = { + port = lib.mkOption { + type = types.port; + default = 9001; + description = "The TCP port for the web console."; + }; + + enable = lib.mkEnableOption "console"; + }; + + accessKey = lib.mkOption { + type = types.str; + default = "rustfsadmin"; + description = "Access key for authentication (5 to 20 characters)."; + }; + + secretKey = lib.mkOption { + type = types.str; + default = "rustfsadmin"; + description = "Secret key for authentication (8 to 40 characters)."; + }; + + extraEnvironment = lib.mkOption { + type = types.attrsOf types.str; + default = { }; + description = '' + Additional environment variables to pass to RustFS. + See the RustFS documentation for available options + (e.g. `RUSTFS_CORS_ALLOWED_ORIGINS`, `RUSTFS_TLS_PATH`). + ''; + example = { + RUSTFS_OBS_LOGGER_LEVEL = "debug"; + RUSTFS_OBJECT_CACHE_ENABLE = "true"; + }; + }; + }; + + config.outputs.settings.processes.${name} = { + environment = { + RUSTFS_ADDRESS = config.server.host; + RUSTFS_PORT = lib.toString config.server.port; + RUSTFS_CONSOLE_ENABLE = if config.console.enable then "true" else "false"; + RUSTFS_CONSOLE_ADDRESS = config.server.host; + RUSTFS_CONSOLE_PORT = lib.toString config.console.port; + RUSTFS_ACCESS_KEY = config.accessKey; + RUSTFS_SECRET_KEY = config.secretKey; + RUSTFS_DATA_DIR = config.dataDir; + } + // config.extraEnvironment; + + command = + # Bash + '' + mkdir -p "$RUSTFS_DATA_DIR" + exec ${config.package}/bin/rustfs "$RUSTFS_DATA_DIR" + ''; + + readiness_probe = { + http_get = { + host = config.server.host; + port = config.server.port; + path = "/health"; + }; + initial_delay_seconds = 1; + period_seconds = 2; + timeout_seconds = 2; + success_threshold = 1; + failure_threshold = 10; + }; + }; +} diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix new file mode 100644 index 00000000..6b805522 --- /dev/null +++ b/nix/services/rustfs_test.nix @@ -0,0 +1,27 @@ +{ lib +, config +, pkgs +, ... +}: +let + cfg = config.services.rustfs.rsfs; +in +{ + services.rustfs."rsfs" = { + package = pkgs.rustfs; + enable = true; + }; + + settings.processes.test = { + command = pkgs.writeShellApplication { + name = "rustfs-test"; + runtimeInputs = [ pkgs.curl ]; + text = '' + echo "Checking if rustfs is up." + curl -sS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" + echo "Rustfs is up." + ''; + }; + depends_on."rsfs".condition = "process_healthy"; + }; +} diff --git a/test/flake.lock b/test/flake.lock index 122c7f3b..b9a15f30 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -49,6 +49,22 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1784364478, + "narHash": "sha256-CdItYNdYUlm7NxqMVyQKqT2IxTwvapiPuRLWOyHTrbY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "20535e48e12c86043b577b8518234ff5dbb26957", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "process-compose-flake": { "locked": { "lastModified": 1767863885, @@ -69,10 +85,51 @@ "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", "process-compose-flake": "process-compose-flake", + "rustfs": "rustfs", "services-flake": "services-flake", "systems": "systems" } }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "rustfs", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1784438913, + "narHash": "sha256-NYF7ZM5ip0u+w1pBFDpIGEbrbgN/wpnLFAmBkWkYMXw=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "afacd6819d3765a05814ee8e3de74c77d42ac799", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rustfs": { + "inputs": { + "nixpkgs": "nixpkgs_2", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1784709136, + "narHash": "sha256-sozRQSAyxLBurWnrwS/xP2zqqtR0r2JCNH9uNwBrCVY=", + "owner": "rustfs", + "repo": "rustfs", + "rev": "92ae19b3408f41e59282401e3f6e92f2b0823feb", + "type": "github" + }, + "original": { + "owner": "rustfs", + "repo": "rustfs", + "type": "github" + } + }, "services-flake": { "locked": { "lastModified": 1778980174, diff --git a/test/flake.nix b/test/flake.nix index bd104f51..4ba27c8d 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,6 +5,8 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; + + rustfs.url = "github:rustfs/rustfs"; }; outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { @@ -69,6 +71,7 @@ "${inputs.services-flake}/nix/services/tika_test.nix" "${inputs.services-flake}/nix/services/weaviate_test.nix" "${inputs.services-flake}/nix/services/zookeeper_test.nix" + "${inputs.services-flake}/nix/services/rustfs_test.nix" ] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ # `phpfpm` test fails on aarch64-darwin: # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index 163a2661..a35e86a1 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -1,25 +1,39 @@ { inputs, ... }: { - perSystem = { self', inputs', pkgs, system, lib, ... }: { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; + perSystem = + { self' + , inputs' + , pkgs + , system + , lib + , ... + }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; - # Required for elastic search - config.allowUnfree = true; + # Required for elastic search + config.allowUnfree = true; - overlays = [ - (self: super: lib.optionalAttrs super.stdenv.isDarwin { + overlays = [ + ( + self: super: + lib.optionalAttrs super.stdenv.isDarwin + { - # Disable tests, because they are failing on darwin: - # https://github.com/NixOS/nixpkgs/issues/281214 - pgadmin4 = super.pgadmin4.overrideAttrs (_: { - doInstallCheck = - false; - }); + # Disable tests, because they are failing on darwin: + # https://github.com/NixOS/nixpkgs/issues/281214 + pgadmin4 = super.pgadmin4.overrideAttrs (_: { + doInstallCheck = false; + }); - }) - ]; + } + // { + rustfs = inputs'.rustfs.packages.default; + } + ) + ]; + }; }; - }; } From ee87bb0bb98d7cd55580a2cba0356912e9cd70d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 11:27:33 +0200 Subject: [PATCH 02/19] chore: add documentation --- doc/rustfs.md | 56 +++++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/doc/rustfs.md b/doc/rustfs.md index 1199bbfb..cf675ba2 100644 --- a/doc/rustfs.md +++ b/doc/rustfs.md @@ -1,45 +1,35 @@ # RustFS -[Qdrant](https://github.com/qdrant/qdrant) is a vector similarity search engine and database for AI applications. +[RustFS](https://github.com/rustfs/rustfs) is a high-performance, distributed object storage system written in Rust. It is compatible with the Amazon S3 API and can be used as a drop-in alternative to MinIO. -Authentik is not in nixpkgs. You must provide the packages yourself via [`components`](#components) from the [`authentik-nix`](https://github.com/nix-community/authentik-nix) flake input. -It also needs a PostgreSQL and a Redis instance. +RustFS is not in nixpkgs. You must provide the package yourself via [`package`](#options), e.g. from the [`rustfs`](https://github.com/rustfs/rustfs) flake input. ## Getting Started ```nix -perSystem = {config, inputs', ...}: -let - ak = config.process-compose."authentik".services.authentik.ak; -in +{ inputs, ... }: { - process-compose."authentik" = { - # Configure the sidecar postgres and a redis processes. - services.postgres = ak.services.postgres; - services.redis = ak.services.redis; - - # Configure authentik. - services.authentik."authentik" = { - enable = true; - - compponents = inputs.authentik-nix.packages.${system}; - secretKey = "dev-secret"; - - settings = { - listen.http = "0.0.0.0:9000"; - - postgresql = { - host = "127.0.0.1"; - port = 5433; - user = "authentik"; - name = "authentik"; - password = "authentik"; - }; - - redis.host = "127.0.0.1"; - redis.port = 6378; + perSystem = { pkgs, system, ... }: { + process-compose."default" = { + services.rustfs."s3" = { + enable = true; + package = inputs.rustfs.packages.${system}.default; + + server.port = 9000; # S3 API + console.enable = true; + console.port = 9001; # Web console + + accessKey = "rustfsadmin"; # 5 to 20 characters + secretKey = "rustfsadmin"; # 8 to 40 characters }; }; }; -` +} ``` + +The S3 API is then available at `http://127.0.0.1:9000` and the web console at +`http://127.0.0.1:9001`. + +## Usage Example + + From 5ef108834aa1e76cbb6449827134e6119d5d8196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 11:35:58 +0200 Subject: [PATCH 03/19] fix: tests with CA certificates --- nix/services/rustfs.nix | 14 ++++++++++++-- nix/services/rustfs_test.nix | 5 +++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 2aa030e3..66bdf09b 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -1,6 +1,7 @@ { config , lib , name +, pkgs , ... }: @@ -71,13 +72,22 @@ in config.outputs.settings.processes.${name} = { environment = { - RUSTFS_ADDRESS = config.server.host; + # RustFS uses `reqwest`, which panics at startup ("No CA certificates were + # loaded from the system") when no CA bundle is available (e.g. inside the + # Nix build sandbox). Point it at nixpkgs' cacert; overridable via + # `extraEnvironment`. + SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + + RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; RUSTFS_PORT = lib.toString config.server.port; + RUSTFS_CONSOLE_ENABLE = if config.console.enable then "true" else "false"; - RUSTFS_CONSOLE_ADDRESS = config.server.host; + RUSTFS_CONSOLE_ADDRESS = "${config.server.host}:${lib.toString config.console.port}"; RUSTFS_CONSOLE_PORT = lib.toString config.console.port; + RUSTFS_ACCESS_KEY = config.accessKey; RUSTFS_SECRET_KEY = config.secretKey; + RUSTFS_DATA_DIR = config.dataDir; } // config.extraEnvironment; diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index 6b805522..641e2aaf 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -12,6 +12,11 @@ in enable = true; }; + settings.processes.rsfs.environment = { + # The test needs CA certificates. + SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + }; + settings.processes.test = { command = pkgs.writeShellApplication { name = "rustfs-test"; From 90a8f74c22e0f1345ee3f1253b8b4e037d4b13e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 11:51:30 +0200 Subject: [PATCH 04/19] fix: tests with CA certificates --- nix/services/rustfs.nix | 7 ------- 1 file changed, 7 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 66bdf09b..c76e7a0e 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -1,7 +1,6 @@ { config , lib , name -, pkgs , ... }: @@ -72,12 +71,6 @@ in config.outputs.settings.processes.${name} = { environment = { - # RustFS uses `reqwest`, which panics at startup ("No CA certificates were - # loaded from the system") when no CA bundle is available (e.g. inside the - # Nix build sandbox). Point it at nixpkgs' cacert; overridable via - # `extraEnvironment`. - SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; - RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; RUSTFS_PORT = lib.toString config.server.port; From 771e9937a761006226ab29ae7ddbf03602c3a43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 13:44:24 +0200 Subject: [PATCH 05/19] fix: correct package argument --- nix/services/rustfs.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index c76e7a0e..cf64f56d 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -11,8 +11,10 @@ in options = { package = lib.mkOption { type = types.package; - description = "Which package of RustFS to use"; - defaultText = lib.literalExpression "inputs.rustfs.packages.\${pkgs.stdenv.hostPlatform.system}.default"; + description = '' + Which package of RustFS to use, + e.g. 'inputs.rustfs.packages.''${pkgs.stdenv.hostPlatform.system}.default'. + ''; }; server = { From dd32c9b247b313c095f4691bf20e264aabb52059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 14:13:45 +0200 Subject: [PATCH 06/19] fix: add non-legacy startup --- nix/services/rustfs.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index cf64f56d..166d45cc 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -91,7 +91,7 @@ in # Bash '' mkdir -p "$RUSTFS_DATA_DIR" - exec ${config.package}/bin/rustfs "$RUSTFS_DATA_DIR" + exec ${config.package}/bin/rustfs server "$RUSTFS_DATA_DIR" ''; readiness_probe = { From b9248d21c6c287b637de33ade5754b43f6ee1ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 14:18:31 +0200 Subject: [PATCH 07/19] fix: set default on console --- nix/services/rustfs.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 166d45cc..d31653d5 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -41,7 +41,11 @@ in description = "The TCP port for the web console."; }; - enable = lib.mkEnableOption "console"; + enable = lib.mkOption { + type = types.bool; + default = true; # This is the default in RustFS. + description = "Enable the console."; + }; }; accessKey = lib.mkOption { From 43ccf7fa7ecec44d1d4385aaf6c666a16569cadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 14:18:47 +0200 Subject: [PATCH 08/19] fix: ports --- nix/services/rustfs.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index d31653d5..678e31ed 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -78,11 +78,9 @@ in config.outputs.settings.processes.${name} = { environment = { RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; - RUSTFS_PORT = lib.toString config.server.port; RUSTFS_CONSOLE_ENABLE = if config.console.enable then "true" else "false"; RUSTFS_CONSOLE_ADDRESS = "${config.server.host}:${lib.toString config.console.port}"; - RUSTFS_CONSOLE_PORT = lib.toString config.console.port; RUSTFS_ACCESS_KEY = config.accessKey; RUSTFS_SECRET_KEY = config.secretKey; From 423fdc8f0dbb0decc4c4ebd10244d74c2e956ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 14:50:45 +0200 Subject: [PATCH 09/19] fix: flake input --- doc/rustfs.md | 6 +- nix/services/rustfs.nix | 12 ++- nix/services/rustfs_test.nix | 7 +- test/flake.lock | 43 +++----- test/flake.nix | 187 +++++++++++++++++++---------------- test/nix/pkgs.nix | 2 +- 6 files changed, 132 insertions(+), 125 deletions(-) diff --git a/doc/rustfs.md b/doc/rustfs.md index cf675ba2..dafe4fb4 100644 --- a/doc/rustfs.md +++ b/doc/rustfs.md @@ -2,7 +2,9 @@ [RustFS](https://github.com/rustfs/rustfs) is a high-performance, distributed object storage system written in Rust. It is compatible with the Amazon S3 API and can be used as a drop-in alternative to MinIO. -RustFS is not in nixpkgs. You must provide the package yourself via [`package`](#options), e.g. from the [`rustfs`](https://github.com/rustfs/rustfs) flake input. +RustFS is not in nixpkgs. You must provide the package yourself +via [`package`](#options), e.g. from the +[`rustfs-flake`](https://github.com/rustfs/rustfs-flake) flake input. ## Getting Started @@ -13,7 +15,7 @@ RustFS is not in nixpkgs. You must provide the package yourself via [`package`]( process-compose."default" = { services.rustfs."s3" = { enable = true; - package = inputs.rustfs.packages.${system}.default; + package = inputs.rustfs-flake.packages.${system}.default; server.port = 9000; # S3 API console.enable = true; diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 678e31ed..304d0451 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -13,7 +13,7 @@ in type = types.package; description = '' Which package of RustFS to use, - e.g. 'inputs.rustfs.packages.''${pkgs.stdenv.hostPlatform.system}.default'. + e.g. 'inputs.rustfs-flake.packages.''${pkgs.stdenv.hostPlatform.system}.default'. ''; }; @@ -60,6 +60,12 @@ in description = "Secret key for authentication (8 to 40 characters)."; }; + logLevel = lib.mkOption { + type = lib.types.str; + default = "info"; + description = "Log level (error, warn, info, debug, trace)."; + }; + extraEnvironment = lib.mkOption { type = types.attrsOf types.str; default = { }; @@ -77,9 +83,9 @@ in config.outputs.settings.processes.${name} = { environment = { + RUST_LOG = config.logLevel; RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; - - RUSTFS_CONSOLE_ENABLE = if config.console.enable then "true" else "false"; + RUSTFS_CONSOLE_ENABLE = lib.boolToString config.console.enable; RUSTFS_CONSOLE_ADDRESS = "${config.server.host}:${lib.toString config.console.port}"; RUSTFS_ACCESS_KEY = config.accessKey; diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index 641e2aaf..8c366f68 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -22,9 +22,14 @@ in name = "rustfs-test"; runtimeInputs = [ pkgs.curl ]; text = '' + set -eu echo "Checking if rustfs is up." - curl -sS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" + curl -sfS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" echo "Rustfs is up." + + echo "Checking if rustfs console is up." + curl -sfS "http://${cfg.server.host}:${lib.toString cfg.console.port}/rustfs/console" + echo "Rustfs console is up." ''; }; depends_on."rsfs".condition = "process_healthy"; diff --git a/test/flake.lock b/test/flake.lock index b9a15f30..404fd64f 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -51,16 +51,16 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784364478, - "narHash": "sha256-CdItYNdYUlm7NxqMVyQKqT2IxTwvapiPuRLWOyHTrbY=", + "lastModified": 1767379071, + "narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "20535e48e12c86043b577b8518234ff5dbb26957", + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-unstable", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } @@ -90,43 +90,22 @@ "systems": "systems" } }, - "rust-overlay": { - "inputs": { - "nixpkgs": [ - "rustfs", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1784438913, - "narHash": "sha256-NYF7ZM5ip0u+w1pBFDpIGEbrbgN/wpnLFAmBkWkYMXw=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "afacd6819d3765a05814ee8e3de74c77d42ac799", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, "rustfs": { "inputs": { - "nixpkgs": "nixpkgs_2", - "rust-overlay": "rust-overlay" + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1784709136, - "narHash": "sha256-sozRQSAyxLBurWnrwS/xP2zqqtR0r2JCNH9uNwBrCVY=", + "lastModified": 1784250516, + "narHash": "sha256-cf2DfGyHKwwnfb6I836u6delfuwtubIcj4WPO/VPG74=", "owner": "rustfs", - "repo": "rustfs", - "rev": "92ae19b3408f41e59282401e3f6e92f2b0823feb", + "repo": "rustfs-flake", + "rev": "f5222f68c19bed705c619412827c4c0d3a33dcd6", "type": "github" }, "original": { "owner": "rustfs", - "repo": "rustfs", + "repo": "rustfs-flake", + "rev": "f5222f68c19bed705c619412827c4c0d3a33dcd6", "type": "github" } }, diff --git a/test/flake.nix b/test/flake.nix index 4ba27c8d..fd09d6d7 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -6,100 +6,115 @@ process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; - rustfs.url = "github:rustfs/rustfs"; + rustfs-flake.url = "github:rustfs/rustfs-flake?rev=f5222f68c19bed705c619412827c4c0d3a33dcd6"; }; - outputs = inputs: + outputs = + inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = import inputs.systems; imports = [ inputs.process-compose-flake.flakeModule ./nix/pkgs.nix ]; - perSystem = { self', inputs', pkgs, system, lib, ... }: { - process-compose = - let - mkPackageFor = mod: - let - # Derive name from filename - name = lib.pipe mod [ - builtins.baseNameOf - (builtins.match "(.*)_test.nix") - builtins.head - ]; - in - lib.nameValuePair name { - imports = [ - inputs.services-flake.processComposeModules.default - mod - ]; - cli = { - options = { - # HTTP server disabled by default but we need it here for tests - no-server = false; - use-uds = true; - unix-socket = "pc-${name}.sock"; + perSystem = + { self' + , inputs' + , pkgs + , system + , lib + , ... + }: + { + process-compose = + let + mkPackageFor = + mod: + let + # Derive name from filename + name = lib.pipe mod [ + builtins.baseNameOf + (builtins.match "(.*)_test.nix") + builtins.head + ]; + in + lib.nameValuePair name { + imports = [ + inputs.services-flake.processComposeModules.default + mod + ]; + cli = { + options = { + # HTTP server disabled by default but we need it here for tests + no-server = false; + use-uds = true; + unix-socket = "pc-${name}.sock"; + }; }; }; - }; - in - builtins.listToAttrs (builtins.map mkPackageFor ([ - "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" - "${inputs.services-flake}/nix/services/azurite_test.nix" - "${inputs.services-flake}/nix/services/chromadb_test.nix" - "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" - "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" - "${inputs.services-flake}/nix/services/elasticmq_test.nix" - "${inputs.services-flake}/nix/services/grafana_test.nix" - "${inputs.services-flake}/nix/services/memcached_test.nix" - "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" - "${inputs.services-flake}/nix/services/nats-server_test.nix" - "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" - "${inputs.services-flake}/nix/services/ollama_test.nix" - "${inputs.services-flake}/nix/services/pgadmin_test.nix" - "${inputs.services-flake}/nix/services/plantuml_test.nix" - "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" - "${inputs.services-flake}/nix/services/prometheus_test.nix" - "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" - "${inputs.services-flake}/nix/services/qdrant_test.nix" - "${inputs.services-flake}/nix/services/neo4j_test.nix" - "${inputs.services-flake}/nix/services/redis_test.nix" - "${inputs.services-flake}/nix/services/redis-cluster_test.nix" - "${inputs.services-flake}/nix/services/searxng_test.nix" - "${inputs.services-flake}/nix/services/pyroscope_test.nix" - "${inputs.services-flake}/nix/services/tempo_test.nix" - "${inputs.services-flake}/nix/services/loki_test.nix" - "${inputs.services-flake}/nix/services/tika_test.nix" - "${inputs.services-flake}/nix/services/weaviate_test.nix" - "${inputs.services-flake}/nix/services/zookeeper_test.nix" - "${inputs.services-flake}/nix/services/rustfs_test.nix" - ] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ - # `phpfpm` test fails on aarch64-darwin: - # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() - # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration - # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed - "${inputs.services-flake}/nix/services/phpfpm_test.nix" - # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` - # Related: https://github.com/NixOS/nix/issues/6765 - "${inputs.services-flake}/nix/services/elasticsearch_test.nix" - # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: - # hostPlatform.system = "aarch64-darwin" - # package.meta.platforms = [ ] - # package.meta.badPlatforms = [ - # "x86_64-darwin" - # "aarch64-darwin" - # ] - "${inputs.services-flake}/nix/services/open-webui_test.nix" - "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 - ] - # Tests on non-linux host only - ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ - # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 - "${inputs.services-flake}/nix/services/mongodb_test.nix" - ])); - }; + in + builtins.listToAttrs ( + builtins.map mkPackageFor ( + [ + "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" + "${inputs.services-flake}/nix/services/azurite_test.nix" + "${inputs.services-flake}/nix/services/chromadb_test.nix" + "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" + "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" + "${inputs.services-flake}/nix/services/elasticmq_test.nix" + "${inputs.services-flake}/nix/services/grafana_test.nix" + "${inputs.services-flake}/nix/services/memcached_test.nix" + "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" + "${inputs.services-flake}/nix/services/nats-server_test.nix" + "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" + "${inputs.services-flake}/nix/services/ollama_test.nix" + "${inputs.services-flake}/nix/services/pgadmin_test.nix" + "${inputs.services-flake}/nix/services/plantuml_test.nix" + "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" + "${inputs.services-flake}/nix/services/prometheus_test.nix" + "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" + "${inputs.services-flake}/nix/services/qdrant_test.nix" + "${inputs.services-flake}/nix/services/neo4j_test.nix" + "${inputs.services-flake}/nix/services/redis_test.nix" + "${inputs.services-flake}/nix/services/redis-cluster_test.nix" + "${inputs.services-flake}/nix/services/searxng_test.nix" + "${inputs.services-flake}/nix/services/pyroscope_test.nix" + "${inputs.services-flake}/nix/services/tempo_test.nix" + "${inputs.services-flake}/nix/services/loki_test.nix" + "${inputs.services-flake}/nix/services/tika_test.nix" + "${inputs.services-flake}/nix/services/weaviate_test.nix" + "${inputs.services-flake}/nix/services/zookeeper_test.nix" + "${inputs.services-flake}/nix/services/rustfs_test.nix" + ] + ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + # `phpfpm` test fails on aarch64-darwin: + # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() + # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration + # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed + "${inputs.services-flake}/nix/services/phpfpm_test.nix" + # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` + # Related: https://github.com/NixOS/nix/issues/6765 + "${inputs.services-flake}/nix/services/elasticsearch_test.nix" + # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: + # hostPlatform.system = "aarch64-darwin" + # package.meta.platforms = [ ] + # package.meta.badPlatforms = [ + # "x86_64-darwin" + # "aarch64-darwin" + # ] + "${inputs.services-flake}/nix/services/open-webui_test.nix" + "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 + ] + # Tests on non-linux host only + ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ + # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 + "${inputs.services-flake}/nix/services/mongodb_test.nix" + ] + ) + ); + }; }; } diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index a35e86a1..fd93f7b0 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -30,7 +30,7 @@ } // { - rustfs = inputs'.rustfs.packages.default; + rustfs = inputs'.rustfs-flake.packages.default; } ) ]; From cc61e0d2f14ba7552d9d4d17b7c78e2d6d5118e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 15:32:28 +0200 Subject: [PATCH 10/19] fix: make proper derivation script --- nix/services/rustfs.nix | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 304d0451..75ca63c7 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -1,4 +1,5 @@ -{ config +{ pkgs +, config , lib , name , ... @@ -95,12 +96,15 @@ in } // config.extraEnvironment; - command = - # Bash - '' - mkdir -p "$RUSTFS_DATA_DIR" - exec ${config.package}/bin/rustfs server "$RUSTFS_DATA_DIR" - ''; + command = pkgs.writeShellApplication { + name = "rustfs"; + text = + # Bash + '' + mkdir -p "$RUSTFS_DATA_DIR" + exec ${config.package}/bin/rustfs server "$RUSTFS_DATA_DIR" + ''; + }; readiness_probe = { http_get = { From 62a56c4c8ed1664e0df1a35ea10bffa40924c3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 16:33:16 +0200 Subject: [PATCH 11/19] fix: WIP --- nix/services/rustfs.nix | 131 ++++++++++++++++++++++++++++++++--- nix/services/rustfs_test.nix | 35 ++++++++-- 2 files changed, 151 insertions(+), 15 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 75ca63c7..4ab7e84e 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -6,11 +6,11 @@ }: let - inherit (lib) types; + inherit (lib) types mkOption mkEnableOption; in { options = { - package = lib.mkOption { + package = mkOption { type = types.package; description = '' Which package of RustFS to use, @@ -19,7 +19,7 @@ in }; server = { - host = lib.mkOption { + host = mkOption { type = types.nullOr types.str; default = "127.0.0.1"; description = '' @@ -28,7 +28,7 @@ in ''; }; - port = lib.mkOption { + port = mkOption { type = types.port; default = 9000; description = "The TCP port for the S3 API."; @@ -36,38 +36,86 @@ in }; console = { - port = lib.mkOption { + port = mkOption { type = types.port; default = 9001; description = "The TCP port for the web console."; }; - enable = lib.mkOption { + enable = mkOption { type = types.bool; default = true; # This is the default in RustFS. description = "Enable the console."; }; }; - accessKey = lib.mkOption { + accessKey = mkOption { type = types.str; default = "rustfsadmin"; description = "Access key for authentication (5 to 20 characters)."; }; - secretKey = lib.mkOption { + secretKey = mkOption { type = types.str; default = "rustfsadmin"; description = "Secret key for authentication (8 to 40 characters)."; }; - logLevel = lib.mkOption { - type = lib.types.str; + logLevel = mkOption { + type = types.str; default = "info"; description = "Log level (error, warn, info, debug, trace)."; }; - extraEnvironment = lib.mkOption { + region = mkOption { + type = types.str; + default = "us-east-1"; + description = "The service region reported to clients."; + }; + + provision = { + enable = mkEnableOption "rustfs provisioning (buckets + IAM blueprint) on startup"; + + buckets = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Buckets to create on startup (idempotent)."; + example = [ + "uploads" + "assets" + ]; + }; + + iam = { + path = lib.mkOption { + type = types.nullOr ( + types.either + (types.pathWith { + inStore = false; + absolute = false; + }) + # A nix store path. + (types.pathWith { inStore = true; }) + ); + default = null; + description = '' + Path to the folder from RustFS IAM export (unzipped) to restore via the admin `import-iam` endpoint on startup. + Produce it via the console IAM export tab. + Import is get-or-create, so it is safe to re-apply on an already-populated data dir. + ''; + }; + }; + + extraScript = lib.mkOption { + type = types.nullOr types.package; + default = null; + description = '' + Extra script with custom provisioning steps. + ''; + }; + }; + + extraEnvironment = mkOption { type = types.attrsOf types.str; default = { }; description = '' @@ -93,6 +141,8 @@ in RUSTFS_SECRET_KEY = config.secretKey; RUSTFS_DATA_DIR = config.dataDir; + + RUSTFS_REGION = config.region; } // config.extraEnvironment; @@ -119,4 +169,63 @@ in failure_threshold = 10; }; }; + + config.outputs.settings.processes."${name}-provision" = lib.mkIf config.provision.enable { + command = pkgs.writeShellApplication { + name = "rustfs-provision"; + runtimeInputs = [ + pkgs.curl + pkgs.minio-client + pkgs.zip + ]; + text = + # Bash + '' + endpoint="${config.server.host}:${lib.toString config.server.port}" + + # Throwaway --config-dir so nothing is written to $HOME. + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT + + export MC_HOST_rustfs="http://${config.accessKey}:${config.secretKey}@$endpoint" + '' + + lib.concatStringsSep "\n" ( + lib.map + ( + b: + # Bash + '' + echo "Provision: Ensuring bucket 'rustfs/${b}'." + mc --config-dir "$tmp" --ignore-existing "rustfs/${b}" + '' + ) + config.provision.buckets + ) + + (lib.optionalString (config.provision.iam.path != null) '' + echo "Provision: Importing IAM from zipping '${config.provision.iam.path}'" + + endpoint="http://${config.server.host}:${lib.toString config.server.port}" + zip -rq "$tmp/iam.zip" "${config.provision.iam.path}" + + curl -fsS -X PUT \ + --aws-sigv4 "aws:amz:${config.region}:s3" \ + -u "${config.accessKey}:${config.secretKey}" \ + --data-binary "@$tmp/iam.zip" \ + -H "Content-Type: application/zip" \ + "http://$endpoint/rustfs/admin/v3/import-iam" + + echo "Provision: IAM import done." + '') + + (lib.optionalString + ( + config.provision.extraScript != null + ) "${lib.getExe config.provision.extraScript}") + + '' + echo "Provision: Done." + ''; + }; + + depends_on.${name}.condition = "process_healthy"; + availability.restart = "no"; + }; } diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index 8c366f68..580a13ad 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -8,8 +8,16 @@ let in { services.rustfs."rsfs" = { - package = pkgs.rustfs; enable = true; + package = pkgs.rustfs; + + provision = { + enable = true; + buckets = [ + "test-a" + "test-b" + ]; + }; }; settings.processes.rsfs.environment = { @@ -20,18 +28,37 @@ in settings.processes.test = { command = pkgs.writeShellApplication { name = "rustfs-test"; - runtimeInputs = [ pkgs.curl ]; + + runtimeInputs = [ + pkgs.curl + pkgs.minio-client + pkgs.gnugrep + ]; + text = '' set -eu echo "Checking if rustfs is up." - curl -sfS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" + curl -fsS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" echo "Rustfs is up." echo "Checking if rustfs console is up." - curl -sfS "http://${cfg.server.host}:${lib.toString cfg.console.port}/rustfs/console" + curl -fsS "http://${cfg.server.host}:${lib.toString cfg.console.port}/rustfs/console" echo "Rustfs console is up." + + echo "Check buckets." + endpoint="${cfg.server.host}:${lib.toString cfg.server.port}" + export MC_HOST_rustfs="http://${cfg.accessKey}:${cfg.secretKey}@$endpoint" + out=$(mc ls rustfs) + for b in ${lib.escapeShellArgs cfg.provision.buckets}; do + if echo "$out" | grep -q "$b"; then + echo "!! Bucket '$b' not listed."; + exit 1 + fi + done + echo "All buckets created." ''; }; depends_on."rsfs".condition = "process_healthy"; + depends_on."rsfs-provision".condition = "process_completed"; }; } From 4a3b655af19813c11284c1fec86aa25356274c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 16:53:50 +0200 Subject: [PATCH 12/19] fix: add buckets and iam provisioning with awscli2 --- nix/services/rustfs.nix | 15 ++++++++------- nix/services/rustfs_test.nix | 17 ++++++++++------- test/flake.lock | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 4ab7e84e..da67ae38 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -175,7 +175,7 @@ in name = "rustfs-provision"; runtimeInputs = [ pkgs.curl - pkgs.minio-client + pkgs.awscli2 pkgs.zip ]; text = @@ -183,11 +183,13 @@ in '' endpoint="${config.server.host}:${lib.toString config.server.port}" - # Throwaway --config-dir so nothing is written to $HOME. + # Scratch dir (for the IAM zip); nothing is written to $HOME. tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT - export MC_HOST_rustfs="http://${config.accessKey}:${config.secretKey}@$endpoint" + export AWS_ACCESS_KEY_ID="${config.accessKey}" + export AWS_SECRET_ACCESS_KEY="${config.secretKey}" + export AWS_DEFAULT_REGION="${config.region}" '' + lib.concatStringsSep "\n" ( lib.map @@ -195,16 +197,15 @@ in b: # Bash '' - echo "Provision: Ensuring bucket 'rustfs/${b}'." - mc --config-dir "$tmp" --ignore-existing "rustfs/${b}" + echo "Provision: Ensuring bucket '${b}'." + aws --endpoint-url "http://$endpoint" s3 mb "s3://${b}" 2>/dev/null + echo "Provision: Bucket '${b}' created." '' ) config.provision.buckets ) + (lib.optionalString (config.provision.iam.path != null) '' echo "Provision: Importing IAM from zipping '${config.provision.iam.path}'" - - endpoint="http://${config.server.host}:${lib.toString config.server.port}" zip -rq "$tmp/iam.zip" "${config.provision.iam.path}" curl -fsS -X PUT \ diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index 580a13ad..9e4b6b2e 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -31,27 +31,30 @@ in runtimeInputs = [ pkgs.curl - pkgs.minio-client pkgs.gnugrep + pkgs.awscli2 ]; text = '' set -eu echo "Checking if rustfs is up." - curl -fsS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" + curl -fsS "http://${cfg.server.host}:${lib.toString cfg.server.port}/health" >/dev/null echo "Rustfs is up." echo "Checking if rustfs console is up." - curl -fsS "http://${cfg.server.host}:${lib.toString cfg.console.port}/rustfs/console" + curl -fsS "http://${cfg.server.host}:${lib.toString cfg.console.port}/rustfs/console" >/dev/null echo "Rustfs console is up." echo "Check buckets." + export AWS_ACCESS_KEY_ID="${cfg.accessKey}" + export AWS_SECRET_ACCESS_KEY="${cfg.secretKey}" + export AWS_DEFAULT_REGION="${cfg.region}" + endpoint="${cfg.server.host}:${lib.toString cfg.server.port}" - export MC_HOST_rustfs="http://${cfg.accessKey}:${cfg.secretKey}@$endpoint" - out=$(mc ls rustfs) + out=$(aws --endpoint-url "http://$endpoint" s3api list-buckets --query 'Buckets[].Name' --output text) for b in ${lib.escapeShellArgs cfg.provision.buckets}; do - if echo "$out" | grep -q "$b"; then - echo "!! Bucket '$b' not listed."; + if ! grep -qw "$b" <<<"$out"; then + echo "!! Bucket '$b' not listed." exit 1 fi done diff --git a/test/flake.lock b/test/flake.lock index 404fd64f..f6a3c96f 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -85,12 +85,12 @@ "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", "process-compose-flake": "process-compose-flake", - "rustfs": "rustfs", + "rustfs-flake": "rustfs-flake", "services-flake": "services-flake", "systems": "systems" } }, - "rustfs": { + "rustfs-flake": { "inputs": { "nixpkgs": "nixpkgs_2" }, From e5eb9226df801ed0eba728d0f24497b766018819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 17:00:22 +0200 Subject: [PATCH 13/19] fix: add iam import to test --- nix/services/rustfs.nix | 276 +++++++++++------- .../iam-export/iam-assets/group_mappings.json | 1 + .../test/iam-export/iam-assets/groups.json | 1 + .../test/iam-export/iam-assets/policies.json | 1 + .../iam-assets/stsuser_mappings.json | 1 + .../test/iam-export/iam-assets/svcaccts.json | 1 + .../iam-export/iam-assets/user_mappings.json | 1 + .../test/iam-export/iam-assets/users.json | 1 + nix/services/rustfs_test.nix | 16 +- 9 files changed, 179 insertions(+), 120 deletions(-) create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/group_mappings.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/groups.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/policies.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/stsuser_mappings.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/svcaccts.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/user_mappings.json create mode 100644 nix/services/rustfs/test/iam-export/iam-assets/users.json diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index da67ae38..50c4d918 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -7,6 +7,9 @@ let inherit (lib) types mkOption mkEnableOption; + + provisionEnable = + config.buckets != [ ] || config.iam.import.path != null || config.provisionScript != null; in { options = { @@ -73,21 +76,19 @@ in description = "The service region reported to clients."; }; - provision = { - enable = mkEnableOption "rustfs provisioning (buckets + IAM blueprint) on startup"; - - buckets = lib.mkOption { - type = types.listOf types.str; - default = [ ]; - description = "Buckets to create on startup (idempotent)."; - example = [ - "uploads" - "assets" - ]; - }; + buckets = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Buckets to create on startup."; + example = [ + "uploads" + "assets" + ]; + }; - iam = { - path = lib.mkOption { + iam = { + import = { + path = mkOption { type = types.nullOr ( types.either (types.pathWith { @@ -106,15 +107,30 @@ in }; }; - extraScript = lib.mkOption { - type = types.nullOr types.package; - default = null; - description = '' - Extra script with custom provisioning steps. - ''; + export = { + enable = mkEnableOption "export of IAM settings on a process '${name}-iam-export'."; + path = mkOption { + type = types.pathWith { + inStore = false; + absolute = false; + }; + default = "${config.dataDir}/export/iam-settings"; + description = '' + Path to the folder where to unzip the RustFS IAM export when the + manual process '${name}-iam-export runs'. + ''; + }; }; }; + provisionScript = mkOption { + type = types.nullOr types.package; + default = null; + description = '' + Extra provision script with custom provisioning steps. + ''; + }; + extraEnvironment = mkOption { type = types.attrsOf types.str; default = { }; @@ -130,103 +146,139 @@ in }; }; - config.outputs.settings.processes.${name} = { - environment = { - RUST_LOG = config.logLevel; - RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; - RUSTFS_CONSOLE_ENABLE = lib.boolToString config.console.enable; - RUSTFS_CONSOLE_ADDRESS = "${config.server.host}:${lib.toString config.console.port}"; - - RUSTFS_ACCESS_KEY = config.accessKey; - RUSTFS_SECRET_KEY = config.secretKey; - - RUSTFS_DATA_DIR = config.dataDir; - - RUSTFS_REGION = config.region; - } - // config.extraEnvironment; - - command = pkgs.writeShellApplication { - name = "rustfs"; - text = - # Bash - '' - mkdir -p "$RUSTFS_DATA_DIR" - exec ${config.package}/bin/rustfs server "$RUSTFS_DATA_DIR" - ''; - }; + config.outputs.settings.processes = { + ${name} = { + environment = { + RUST_LOG = config.logLevel; + RUSTFS_ADDRESS = "${config.server.host}:${lib.toString config.server.port}"; + RUSTFS_CONSOLE_ENABLE = lib.boolToString config.console.enable; + RUSTFS_CONSOLE_ADDRESS = "${config.server.host}:${lib.toString config.console.port}"; + + RUSTFS_ACCESS_KEY = config.accessKey; + RUSTFS_SECRET_KEY = config.secretKey; + + RUSTFS_DATA_DIR = config.dataDir; - readiness_probe = { - http_get = { - host = config.server.host; - port = config.server.port; - path = "/health"; + RUSTFS_REGION = config.region; + } + // config.extraEnvironment; + + command = pkgs.writeShellApplication { + name = "rustfs"; + text = + # Bash + '' + mkdir -p "$RUSTFS_DATA_DIR" + exec ${config.package}/bin/rustfs server "$RUSTFS_DATA_DIR" + ''; + }; + + readiness_probe = { + http_get = { + host = config.server.host; + port = config.server.port; + path = "/health"; + }; + initial_delay_seconds = 1; + period_seconds = 2; + timeout_seconds = 2; + success_threshold = 1; + failure_threshold = 10; }; - initial_delay_seconds = 1; - period_seconds = 2; - timeout_seconds = 2; - success_threshold = 1; - failure_threshold = 10; }; - }; + } + // lib.optionalAttrs provisionEnable { + "${name}-provision" = { + command = pkgs.writeShellApplication { + name = "rustfs-provision"; + runtimeInputs = [ + pkgs.curl + pkgs.awscli2 + pkgs.zip + ]; + text = + # Bash + '' + # shellcheck disable=SC2034 + endpoint="${config.server.host}:${lib.toString config.server.port}" - config.outputs.settings.processes."${name}-provision" = lib.mkIf config.provision.enable { - command = pkgs.writeShellApplication { - name = "rustfs-provision"; - runtimeInputs = [ - pkgs.curl - pkgs.awscli2 - pkgs.zip - ]; - text = - # Bash - '' - endpoint="${config.server.host}:${lib.toString config.server.port}" - - # Scratch dir (for the IAM zip); nothing is written to $HOME. - tmp="$(mktemp -d)" - trap 'rm -rf "$tmp"' EXIT - - export AWS_ACCESS_KEY_ID="${config.accessKey}" - export AWS_SECRET_ACCESS_KEY="${config.secretKey}" - export AWS_DEFAULT_REGION="${config.region}" - '' - + lib.concatStringsSep "\n" ( - lib.map - ( - b: - # Bash - '' - echo "Provision: Ensuring bucket '${b}'." - aws --endpoint-url "http://$endpoint" s3 mb "s3://${b}" 2>/dev/null - echo "Provision: Bucket '${b}' created." - '' - ) - config.provision.buckets - ) - + (lib.optionalString (config.provision.iam.path != null) '' - echo "Provision: Importing IAM from zipping '${config.provision.iam.path}'" - zip -rq "$tmp/iam.zip" "${config.provision.iam.path}" - - curl -fsS -X PUT \ - --aws-sigv4 "aws:amz:${config.region}:s3" \ - -u "${config.accessKey}:${config.secretKey}" \ - --data-binary "@$tmp/iam.zip" \ - -H "Content-Type: application/zip" \ - "http://$endpoint/rustfs/admin/v3/import-iam" - - echo "Provision: IAM import done." - '') - + (lib.optionalString - ( - config.provision.extraScript != null - ) "${lib.getExe config.provision.extraScript}") - + '' - echo "Provision: Done." - ''; + # Scratch dir (for the IAM zip); nothing is written to $HOME. + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT + + export AWS_ACCESS_KEY_ID="${config.accessKey}" + export AWS_SECRET_ACCESS_KEY="${config.secretKey}" + export AWS_DEFAULT_REGION="${config.region}" + '' + + lib.concatStringsSep "\n" ( + lib.map + ( + b: + # Bash + '' + echo "Provision: Ensuring bucket '${b}'." + aws --endpoint-url "http://$endpoint" s3 mb "s3://${b}" 2>/dev/null + echo "Provision: Bucket '${b}' created." + '' + ) + config.buckets + ) + + (lib.optionalString (config.iam.import.path != null) '' + echo "Provision: Importing IAM from zipping '${config.iam.import.path}'" + zip -rq "$tmp/iam.zip" "${config.iam.import.path}" + + curl -fsS -X PUT \ + --aws-sigv4 "aws:amz:${config.region}:s3" \ + -u "${config.accessKey}:${config.secretKey}" \ + --data-binary "@$tmp/iam.zip" \ + -H "Content-Type: application/zip" \ + "http://$endpoint/rustfs/admin/v3/import-iam" + + echo "Provision: IAM import done." + '') + + (lib.optionalString (config.provisionScript != null) "${lib.getExe config.provisionScript}") + + '' + echo "Provision: Done." + ''; + }; + + depends_on.${name}.condition = "process_healthy"; + availability.restart = "no"; }; + } + // lib.optionalAttrs config.iam.export.enable { + "${name}-iam-export" = { + command = pkgs.writeShellApplication { + name = "${name}-iam-export"; + runtimeInputs = [ + pkgs.curl + pkgs.unzip + ]; + text = + # Bash + '' + endpoint="${config.server.host}:${lib.toString config.server.port}" + + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT - depends_on.${name}.condition = "process_healthy"; - availability.restart = "no"; + # IAM export — SigV4-signed GET from the admin export endpoint. + echo "Export: Downloading IAM settings into '${config.iam.export.path}'." + curl -fsS -X GET \ + --aws-sigv4 "aws:amz:${config.region}:s3" \ + -u "${config.accessKey}:${config.secretKey}" \ + -H "Accept: application/zip" \ + -o "$tmp/iam.zip" \ + "http://$endpoint/rustfs/admin/v3/export-iam" + + echo "Unzipping into '${config.iam.export.path}'." + mkdir -p "${config.iam.export.path}" + unzip -oq "$tmp/iam.zip" -d "${config.iam.export.path}" + + echo "Export: IAM export done." + ''; + }; + disabled = true; + }; }; } diff --git a/nix/services/rustfs/test/iam-export/iam-assets/group_mappings.json b/nix/services/rustfs/test/iam-export/iam-assets/group_mappings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/group_mappings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/groups.json b/nix/services/rustfs/test/iam-export/iam-assets/groups.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/groups.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/policies.json b/nix/services/rustfs/test/iam-export/iam-assets/policies.json new file mode 100644 index 00000000..90278240 --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/policies.json @@ -0,0 +1 @@ +{"readwrite":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::*"]},{"Effect":"Allow","Action":["sts:AssumeRole"]}]},"diagnostics":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["admin:Profiling","admin:ServerTrace","admin:ConsoleLog","admin:ServerInfo","admin:TopLocksInfo","admin:OBDInfo","admin:Prometheus","admin:BandwidthMonitor"],"Resource":["arn:aws:s3:::*"]},{"Effect":"Allow","Action":["sts:AssumeRole"]}]},"writeonly":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:PutObject"],"Resource":["arn:aws:s3:::*"]},{"Effect":"Allow","Action":["sts:AssumeRole"]}]},"consoleAdmin":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["admin:*"]},{"Effect":"Allow","Action":["kms:*"]},{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::*"]},{"Effect":"Allow","Action":["sts:AssumeRole"]}]},"readonly":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetBucketLocation","s3:GetObject","s3:GetBucketQuota"],"Resource":["arn:aws:s3:::*"]},{"Effect":"Allow","Action":["sts:AssumeRole"]}]}} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/stsuser_mappings.json b/nix/services/rustfs/test/iam-export/iam-assets/stsuser_mappings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/stsuser_mappings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/svcaccts.json b/nix/services/rustfs/test/iam-export/iam-assets/svcaccts.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/svcaccts.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/user_mappings.json b/nix/services/rustfs/test/iam-export/iam-assets/user_mappings.json new file mode 100644 index 00000000..742dd881 --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/user_mappings.json @@ -0,0 +1 @@ +{"test":{"version":1,"policy":"writeonly","updatedAt":"2026-07-22T14:58:06.912165643Z"}} \ No newline at end of file diff --git a/nix/services/rustfs/test/iam-export/iam-assets/users.json b/nix/services/rustfs/test/iam-export/iam-assets/users.json new file mode 100644 index 00000000..11700ee1 --- /dev/null +++ b/nix/services/rustfs/test/iam-export/iam-assets/users.json @@ -0,0 +1 @@ +{"test":{"secretKey":"testtest","status":"enabled"}} \ No newline at end of file diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index 9e4b6b2e..d0097147 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -11,13 +11,13 @@ in enable = true; package = pkgs.rustfs; - provision = { - enable = true; - buckets = [ - "test-a" - "test-b" - ]; - }; + buckets = [ + "test-a" + "test-b" + ]; + + iam.import.path = ./rustfs/test/iam-export; + iam.export.enable = true; }; settings.processes.rsfs.environment = { @@ -52,7 +52,7 @@ in endpoint="${cfg.server.host}:${lib.toString cfg.server.port}" out=$(aws --endpoint-url "http://$endpoint" s3api list-buckets --query 'Buckets[].Name' --output text) - for b in ${lib.escapeShellArgs cfg.provision.buckets}; do + for b in ${lib.escapeShellArgs cfg.buckets}; do if ! grep -qw "$b" <<<"$out"; then echo "!! Bucket '$b' not listed." exit 1 From 8324c8fdde5c456a981595db2bccb49b7174a272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 18:04:08 +0200 Subject: [PATCH 14/19] fix: provisioning steps and export --- nix/services/rustfs.nix | 2 +- nix/services/rustfs_test.nix | 43 +++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 50c4d918..25c22c5a 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -157,7 +157,7 @@ in RUSTFS_ACCESS_KEY = config.accessKey; RUSTFS_SECRET_KEY = config.secretKey; - RUSTFS_DATA_DIR = config.dataDir; + RUSTFS_DATA_DIR = "${config.dataDir}/data"; RUSTFS_REGION = config.region; } diff --git a/nix/services/rustfs_test.nix b/nix/services/rustfs_test.nix index d0097147..e7940f2c 100644 --- a/nix/services/rustfs_test.nix +++ b/nix/services/rustfs_test.nix @@ -5,6 +5,8 @@ }: let cfg = config.services.rustfs.rsfs; + name = "rsfs"; + exportPath = config.services.rustfs.${name}.iam.export.path; in { services.rustfs."rsfs" = { @@ -20,7 +22,7 @@ in iam.export.enable = true; }; - settings.processes.rsfs.environment = { + settings.processes.${name}.environment = { # The test needs CA certificates. SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; }; @@ -33,6 +35,7 @@ in pkgs.curl pkgs.gnugrep pkgs.awscli2 + pkgs.jq ]; text = '' @@ -59,6 +62,44 @@ in fi done echo "All buckets created." + + + export PC_SOCKET_PATH="${config.cli.options.unix-socket}" + # Silence process-compose not finding a config home. + mkdir -p "$(pwd)/.config/process-compose" + # shellcheck disable=SC2155 + export XDG_CONFIG_HOME="$(pwd)/.config" + + echo "Check export." + process-compose process start "${name}-iam-export" + + completed="false" + for _ in $(seq 1 30); do + if + [ "$( + process-compose process get "${name}-iam-export" \ + -o json | + jq -r ".[0].status" + )" = "Completed" ] + then + completed="true" + break + fi + + sleep 2 + done + + if [ "$completed" != "true" ]; then + echo "!! Blueprint export did not complete in time." + exit 1 + fi + + # shellcheck disable=SC2010 + if [ ! -d "${exportPath}/iam-assets" ]; then + echo "!! Export dir '${exportPath}' did not get created." + ls "${exportPath}" + exit 1 + fi ''; }; depends_on."rsfs".condition = "process_healthy"; From 7d1324eabd309cc49fa98fa97f186f561bc4e8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:16:36 +0200 Subject: [PATCH 15/19] fix: formatting in flake.nix --- test/flake.nix | 187 ++++++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 102 deletions(-) diff --git a/test/flake.nix b/test/flake.nix index fd09d6d7..e9178677 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,116 +5,99 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; - - rustfs-flake.url = "github:rustfs/rustfs-flake?rev=f5222f68c19bed705c619412827c4c0d3a33dcd6"; }; - outputs = - inputs: + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = import inputs.systems; imports = [ inputs.process-compose-flake.flakeModule ./nix/pkgs.nix ]; - perSystem = - { self' - , inputs' - , pkgs - , system - , lib - , ... - }: - { - process-compose = - let - mkPackageFor = - mod: - let - # Derive name from filename - name = lib.pipe mod [ - builtins.baseNameOf - (builtins.match "(.*)_test.nix") - builtins.head - ]; - in - lib.nameValuePair name { - imports = [ - inputs.services-flake.processComposeModules.default - mod - ]; - cli = { - options = { - # HTTP server disabled by default but we need it here for tests - no-server = false; - use-uds = true; - unix-socket = "pc-${name}.sock"; - }; + perSystem = { self', inputs', pkgs, system, lib, ... }: { + process-compose = + let + mkPackageFor = mod: + let + # Derive name from filename + name = lib.pipe mod [ + builtins.baseNameOf + (builtins.match "(.*)_test.nix") + builtins.head + ]; + in + lib.nameValuePair name { + imports = [ + inputs.services-flake.processComposeModules.default + mod + ]; + cli = { + options = { + # HTTP server disabled by default but we need it here for tests + no-server = false; + use-uds = true; + unix-socket = "pc-${name}.sock"; }; }; - in - builtins.listToAttrs ( - builtins.map mkPackageFor ( - [ - "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" - "${inputs.services-flake}/nix/services/azurite_test.nix" - "${inputs.services-flake}/nix/services/chromadb_test.nix" - "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" - "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" - "${inputs.services-flake}/nix/services/elasticmq_test.nix" - "${inputs.services-flake}/nix/services/grafana_test.nix" - "${inputs.services-flake}/nix/services/memcached_test.nix" - "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" - "${inputs.services-flake}/nix/services/nats-server_test.nix" - "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" - "${inputs.services-flake}/nix/services/ollama_test.nix" - "${inputs.services-flake}/nix/services/pgadmin_test.nix" - "${inputs.services-flake}/nix/services/plantuml_test.nix" - "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" - "${inputs.services-flake}/nix/services/prometheus_test.nix" - "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" - "${inputs.services-flake}/nix/services/qdrant_test.nix" - "${inputs.services-flake}/nix/services/neo4j_test.nix" - "${inputs.services-flake}/nix/services/redis_test.nix" - "${inputs.services-flake}/nix/services/redis-cluster_test.nix" - "${inputs.services-flake}/nix/services/searxng_test.nix" - "${inputs.services-flake}/nix/services/pyroscope_test.nix" - "${inputs.services-flake}/nix/services/tempo_test.nix" - "${inputs.services-flake}/nix/services/loki_test.nix" - "${inputs.services-flake}/nix/services/tika_test.nix" - "${inputs.services-flake}/nix/services/weaviate_test.nix" - "${inputs.services-flake}/nix/services/zookeeper_test.nix" - "${inputs.services-flake}/nix/services/rustfs_test.nix" - ] - ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ - # `phpfpm` test fails on aarch64-darwin: - # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() - # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration - # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed - "${inputs.services-flake}/nix/services/phpfpm_test.nix" - # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` - # Related: https://github.com/NixOS/nix/issues/6765 - "${inputs.services-flake}/nix/services/elasticsearch_test.nix" - # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: - # hostPlatform.system = "aarch64-darwin" - # package.meta.platforms = [ ] - # package.meta.badPlatforms = [ - # "x86_64-darwin" - # "aarch64-darwin" - # ] - "${inputs.services-flake}/nix/services/open-webui_test.nix" - "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 - ] - # Tests on non-linux host only - ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ - # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 - "${inputs.services-flake}/nix/services/mongodb_test.nix" - ] - ) - ); - }; + }; + in + builtins.listToAttrs (builtins.map mkPackageFor ([ + "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" + "${inputs.services-flake}/nix/services/azurite_test.nix" + "${inputs.services-flake}/nix/services/chromadb_test.nix" + "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" + "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" + "${inputs.services-flake}/nix/services/elasticmq_test.nix" + "${inputs.services-flake}/nix/services/grafana_test.nix" + "${inputs.services-flake}/nix/services/memcached_test.nix" + "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" + "${inputs.services-flake}/nix/services/nats-server_test.nix" + "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" + "${inputs.services-flake}/nix/services/ollama_test.nix" + "${inputs.services-flake}/nix/services/pgadmin_test.nix" + "${inputs.services-flake}/nix/services/plantuml_test.nix" + "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" + "${inputs.services-flake}/nix/services/prometheus_test.nix" + "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" + "${inputs.services-flake}/nix/services/qdrant_test.nix" + "${inputs.services-flake}/nix/services/neo4j_test.nix" + "${inputs.services-flake}/nix/services/redis_test.nix" + "${inputs.services-flake}/nix/services/redis-cluster_test.nix" + "${inputs.services-flake}/nix/services/rustfs_test.nix" + "${inputs.services-flake}/nix/services/searxng_test.nix" + "${inputs.services-flake}/nix/services/pyroscope_test.nix" + "${inputs.services-flake}/nix/services/tempo_test.nix" + "${inputs.services-flake}/nix/services/loki_test.nix" + "${inputs.services-flake}/nix/services/tika_test.nix" + "${inputs.services-flake}/nix/services/weaviate_test.nix" + "${inputs.services-flake}/nix/services/zookeeper_test.nix" + ] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + # `phpfpm` test fails on aarch64-darwin: + # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() + # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration + # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed + "${inputs.services-flake}/nix/services/phpfpm_test.nix" + # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` + # Related: https://github.com/NixOS/nix/issues/6765 + "${inputs.services-flake}/nix/services/elasticsearch_test.nix" + # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: + # hostPlatform.system = "aarch64-darwin" + # package.meta.platforms = [ ] + # package.meta.badPlatforms = [ + # "x86_64-darwin" + # "aarch64-darwin" + # ] + "${inputs.services-flake}/nix/services/open-webui_test.nix" + "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 + ] + # Tests on non-linux host only + ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ + # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 + "${inputs.services-flake}/nix/services/mongodb_test.nix" + ])); + }; }; } From 54ec17a9c23de2c5ab15cf87f50f64c3d728d393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:22:04 +0200 Subject: [PATCH 16/19] fix: formatting in pkgs.nix --- test/nix/pkgs.nix | 50 +++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index fd93f7b0..b00e9250 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -1,39 +1,29 @@ { inputs, ... }: { - perSystem = - { self' - , inputs' - , pkgs - , system - , lib - , ... - }: - { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; + perSystem = { self', inputs', pkgs, system, lib, ... }: { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; - # Required for elastic search - config.allowUnfree = true; + # Required for elastic search + config.allowUnfree = true; - overlays = [ - ( - self: super: - lib.optionalAttrs super.stdenv.isDarwin - { + overlays = [ + (self: super: lib.optionalAttrs super.stdenv.isDarwin + { - # Disable tests, because they are failing on darwin: - # https://github.com/NixOS/nixpkgs/issues/281214 - pgadmin4 = super.pgadmin4.overrideAttrs (_: { - doInstallCheck = false; - }); + # Disable tests, because they are failing on darwin: + # https://github.com/NixOS/nixpkgs/issues/281214 + pgadmin4 = super.pgadmin4.overrideAttrs (_: { + doInstallCheck = + false; + }); - } - // { - rustfs = inputs'.rustfs-flake.packages.default; - } - ) - ]; - }; + } // { + rustfs = inputs'.rustfs-flake.packages.default; + } + ) + ]; }; + }; } From 8fb5b15513e9c85da2ea3d356d4df331a6379ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:23:03 +0200 Subject: [PATCH 17/19] fix: formatting in pkgs.nix --- test/nix/pkgs.nix | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index b00e9250..87ed7245 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -19,10 +19,7 @@ false; }); - } // { - rustfs = inputs'.rustfs-flake.packages.default; - } - ) + } // { rustfs = inputs'.rustfs-flake.packages.default; }) ]; }; }; From 59fb0b85489afdc2877af6a7c3abc023bbac865a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:23:54 +0200 Subject: [PATCH 18/19] fix: formatting in pkgs.nix --- test/nix/pkgs.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index 87ed7245..914436d8 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -19,7 +19,10 @@ false; }); - } // { rustfs = inputs'.rustfs-flake.packages.default; }) + } // + { + rustfs = inputs'.rustfs-flake.packages.default; + }) ]; }; }; From a68d9171395da082284bf3b4f78d7e9f0aee18c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Mon, 27 Jul 2026 15:22:01 +0200 Subject: [PATCH 19/19] fix: zipping bug in import --- nix/services/rustfs.nix | 23 ++++++++++++++--------- test/flake.lock | 7 +++---- test/flake.nix | 1 + 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/nix/services/rustfs.nix b/nix/services/rustfs.nix index 25c22c5a..46db0a61 100644 --- a/nix/services/rustfs.nix +++ b/nix/services/rustfs.nix @@ -224,17 +224,22 @@ in config.buckets ) + (lib.optionalString (config.iam.import.path != null) '' - echo "Provision: Importing IAM from zipping '${config.iam.import.path}'" - zip -rq "$tmp/iam.zip" "${config.iam.import.path}" + if [ -d "${config.iam.import.path}" ]; then + src="${config.iam.import.path}" + echo "Provision: Importing IAM from zipping '$src'" + (cd "${config.iam.import.path}" && zip -rq "$tmp/iam.zip" .) - curl -fsS -X PUT \ - --aws-sigv4 "aws:amz:${config.region}:s3" \ - -u "${config.accessKey}:${config.secretKey}" \ - --data-binary "@$tmp/iam.zip" \ - -H "Content-Type: application/zip" \ - "http://$endpoint/rustfs/admin/v3/import-iam" + curl -fsS -X PUT \ + --aws-sigv4 "aws:amz:${config.region}:s3" \ + -u "${config.accessKey}:${config.secretKey}" \ + --data-binary "@$tmp/iam.zip" \ + -H "Content-Type: application/zip" \ + "http://$endpoint/rustfs/admin/v3/import-iam" - echo "Provision: IAM import done." + echo "Provision: IAM import done." + else + echo "Provision: IAM import: path '$src' does not exist." + fi '') + (lib.optionalString (config.provisionScript != null) "${lib.getExe config.provisionScript}") + '' diff --git a/test/flake.lock b/test/flake.lock index f6a3c96f..8411941f 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -95,17 +95,16 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1784250516, - "narHash": "sha256-cf2DfGyHKwwnfb6I836u6delfuwtubIcj4WPO/VPG74=", + "lastModified": 1784874492, + "narHash": "sha256-E4kC+rya1KfPpDGWtq+E3Ka4HoJGVIo5f9ZsHzYivUs=", "owner": "rustfs", "repo": "rustfs-flake", - "rev": "f5222f68c19bed705c619412827c4c0d3a33dcd6", + "rev": "7dedccc7160410a325f922ed8591d90152a261b7", "type": "github" }, "original": { "owner": "rustfs", "repo": "rustfs-flake", - "rev": "f5222f68c19bed705c619412827c4c0d3a33dcd6", "type": "github" } }, diff --git a/test/flake.nix b/test/flake.nix index e9178677..e4fb9e6d 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,6 +5,7 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; + rustfs-flake.url = "github:rustfs/rustfs-flake?rev=f5222f68c19bed705c619412827c4c0d3a33dcd6"; }; outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } {