From 8ac0a2ec2b3370667ea4da7e3c2af5ca79848a70 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Dec 2025 02:01:02 +0100 Subject: [PATCH 1/2] rustfs: init at 1.0.0-beta.9 --- pkgs/by-name/ru/rustfs/package.nix | 100 +++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 pkgs/by-name/ru/rustfs/package.nix diff --git a/pkgs/by-name/ru/rustfs/package.nix b/pkgs/by-name/ru/rustfs/package.nix new file mode 100644 index 000000000000..1a973ddb5281 --- /dev/null +++ b/pkgs/by-name/ru/rustfs/package.nix @@ -0,0 +1,100 @@ +{ + lib, + stdenv, + fetchFromGitHub, + fetchPnpmDeps, + pnpm, + pnpmConfigHook, + nodejs, + rustPlatform, + protobuf, + cacert, +}: + +let + console = stdenv.mkDerivation (finalAttrs: { + pname = "rustfs-console"; + version = "0.1.13"; + __structuredAttrs = true; + __darwinAllowLocalNetworking = true; + + src = fetchFromGitHub { + owner = "rustfs"; + repo = "console"; + tag = "v${finalAttrs.version}"; + hash = "sha256-pxpT3kV30qA+Ob/RWi11rsapGyNc6h1EN79fcPi1e1E="; + }; + + pnpmDeps = fetchPnpmDeps { + inherit (finalAttrs) pname version src; + fetcherVersion = 4; + hash = "sha256-+U4HRaThEeC6jA6dA4UmhJLvANq0IMySOW5ua9m5Q6A="; + }; + + nativeBuildInputs = [ + nodejs + pnpm + pnpmConfigHook + ]; + + buildPhase = '' + pnpm run build + ''; + + installPhase = '' + runHook preInstall + cp -r out/. $out/ + runHook postInstall + ''; + }); +in +rustPlatform.buildRustPackage rec { + pname = "rustfs"; + version = "1.0.0-beta.9"; + __structuredAttrs = true; + + src = fetchFromGitHub { + owner = "rustfs"; + repo = "rustfs"; + tag = version; + hash = "sha256-aNbicnNHaJn05k5EffgPEURf/Uj2A8PjOHiH2UGPz4M="; + }; + + postPatch = '' + rm -rf ./rustfs/static + cp -rL ${console} ./rustfs/static + ''; + + cargoHash = "sha256-abbsElP4dSSZnL4UfQEoHUtiEW8B/p6Y81UA7EbqbD4="; + + nativeBuildInputs = [ + protobuf + cacert + ]; + + env = { + RUSTFLAGS = "--cfg tokio_unstable"; + # reqwest loads CA certs even if not used during tests + SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + }; + + # Only build the main rustfs binary + cargoBuildFlags = "-p rustfs"; + cargoTestFlags = "-p rustfs"; + + checkFlags = [ + # failing since 1.0.0-beta.9, seem like upstream issues + "--skip=app::capacity_dirty_scope_test" + "--skip=app::delete_objects_stat_gating_test" + "--skip=app::put_prelookup_gating_test" + "--skip=two_embedded_servers_isolate_auth_and_data_planes" + ]; + + meta = { + description = "S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph"; + homepage = "https://github.com/rustfs/rustfs"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [ marcel ]; + mainProgram = "rustfs"; + }; +} From 81b44d852745e661d85729bec374841087e7791e Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Dec 2025 02:02:00 +0100 Subject: [PATCH 2/2] nixos/rustfs: init --- nixos/modules/module-list.nix | 1 + nixos/modules/services/web-servers/rustfs.nix | 151 ++++++++++++++++++ nixos/tests/all-tests.nix | 1 + nixos/tests/rustfs.nix | 69 ++++++++ pkgs/by-name/ru/rustfs/package.nix | 5 + 5 files changed, 227 insertions(+) create mode 100644 nixos/modules/services/web-servers/rustfs.nix create mode 100644 nixos/tests/rustfs.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 1b7c62d6b643..5596db7c60f2 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1854,6 +1854,7 @@ ./services/web-servers/nginx/tailscale-auth.nix ./services/web-servers/phpfpm/default.nix ./services/web-servers/pomerium.nix + ./services/web-servers/rustfs.nix ./services/web-servers/rustus.nix ./services/web-servers/send.nix ./services/web-servers/stargazer.nix diff --git a/nixos/modules/services/web-servers/rustfs.nix b/nixos/modules/services/web-servers/rustfs.nix new file mode 100644 index 000000000000..675a6314426b --- /dev/null +++ b/nixos/modules/services/web-servers/rustfs.nix @@ -0,0 +1,151 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.rustfs; +in +{ + meta.maintainers = with lib.maintainers; [ + marcel + ]; + + options.services.rustfs = { + enable = lib.mkEnableOption "RustFS Object Storage Server"; + + package = lib.mkPackageOption pkgs "rustfs" { }; + + user = lib.mkOption { + type = lib.types.str; + default = "rustfs"; + description = "The user RustFS should run as."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "rustfs"; + description = "The group RustFS should run as."; + }; + + settings = lib.mkOption { + default = { }; + description = '' + Options for RustFS configuration. Refer to + + for details on supported values. + ''; + example = lib.literalExpression '' + { + RUSTFS_CONSOLE_ENABLE = "true"; + RUSTFS_VOLUMES = "/mnt/rustfs"; + } + ''; + type = lib.types.submodule { + freeformType = lib.types.attrsOf ( + lib.types.oneOf [ + lib.types.str + lib.types.int + ] + ); + options = { + RUSTFS_VOLUMES = lib.mkOption { + type = lib.types.path; + default = "/var/lib/rustfs"; + description = "The directory where RustFS stores it's data."; + }; + }; + }; + }; + + environmentFile = lib.mkOption { + type = lib.types.str; + description = "Path to environment file containing secrets like RUSTFS_ACCESS_KEY or RUSTFS_SECRET_KEY."; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.settings ? RUSTFS_ACCESS_KEY); + message = "RUSTFS_ACCESS_KEY must not be set in services.rustfs.settings. Use environmentFile instead."; + } + { + assertion = !(cfg.settings ? RUSTFS_SECRET_KEY); + message = "RUSTFS_SECRET_KEY must not be set in services.rustfs.settings. Use environmentFile instead."; + } + ]; + + systemd = { + tmpfiles.settings."10-rustfs".${cfg.settings.RUSTFS_VOLUMES}.d = { + inherit (cfg) user group; + }; + + # https://docs.rustfs.com/installation/linux/single-node-single-disk.html#_6-configure-system-service + services.rustfs = { + description = "RustFS Object Storage Server"; + documentation = [ "https://rustfs.com/docs/" ]; + + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + + wantedBy = [ "multi-user.target" ]; + + environment = cfg.settings; + + preStart = '' + if [ -z "$RUSTFS_ACCESS_KEY" ] || [ -z "$RUSTFS_SECRET_KEY" ]; then + echo "RustFS uses well-known default values for RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY," + echo "please configure them using services.rustfs.environmentFile." + exit 1 + fi + ''; + + serviceConfig = { + Type = "notify"; + NotifyAccess = "main"; + User = cfg.user; + Group = cfg.group; + + EnvironmentFile = cfg.environmentFile; + ExecStart = lib.getExe cfg.package; + + LimitNOFILE = 1048576; + LimitNPROC = 32768; + TasksMax = "infinity"; + + Restart = "always"; + RestartSec = "10s"; + + OOMScoreAdjust = "-1000"; + SendSIGKILL = false; + + TimeoutStartSec = "30s"; + TimeoutStopSec = "30s"; + + NoNewPrivileges = true; + + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + }; + }; + }; + + users = { + users.${cfg.user} = { + isSystemUser = true; + inherit (cfg) group; + }; + groups.${cfg.group} = { }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5e7a1a12a29c..e3e189a80f65 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1512,6 +1512,7 @@ in rtkit = runTest ./rtkit.nix; rtorrent = runTest ./rtorrent.nix; rush = runTest ./rush.nix; + rustfs = runTest ./rustfs.nix; rustical = runTest ./web-apps/rustical.nix; rustls-libssl = runTest ./rustls-libssl.nix; rxe = runTest ./rxe.nix; diff --git a/nixos/tests/rustfs.nix b/nixos/tests/rustfs.nix new file mode 100644 index 000000000000..7cace30e3bfc --- /dev/null +++ b/nixos/tests/rustfs.nix @@ -0,0 +1,69 @@ +{ pkgs, ... }: + +let + accessKey = "BKIKJAA5BMMU2RHO6IBB"; + secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12"; + + rustfsPythonScript = + pkgs.writers.writePython3 "rustfs-test" { libraries = with pkgs.python3Packages; [ minio ]; } + /* python */ '' + import io + import os + from minio import Minio + + minioClient = Minio( + 'localhost:9000', + access_key='${accessKey}', + secret_key='${secretKey}', + secure=False + ) + sio = io.BytesIO() + sio.write(b'Test from Python') + sio.seek(0, os.SEEK_END) + sio_len = sio.tell() + sio.seek(0) + minioClient.put_object( + 'test-bucket', + 'test.txt', + sio, + sio_len, + content_type='text/plain' + ) + ''; + +in +{ + name = "rustfs"; + meta = with pkgs.lib.maintainers; { + maintainers = [ + marcel + ]; + }; + + containers.machine = + { pkgs, ... }: + { + services.rustfs = { + enable = true; + environmentFile = builtins.toString ( + pkgs.writeText "rustfs-secrets.env" '' + RUSTFS_ACCESS_KEY=${accessKey} + RUSTFS_SECRET_KEY=${secretKey} + '' + ); + }; + + environment.systemPackages = with pkgs; [ minio-client ]; + }; + + testScript = /* python */ '' + machine.wait_for_unit("rustfs.service") + + machine.succeed("mc alias set rustfs http://localhost:9000 ${accessKey} ${secretKey} --api s3v4") + machine.succeed("mc mb rustfs/test-bucket") + machine.succeed("${rustfsPythonScript}") + assert "test-bucket" in machine.succeed("mc ls rustfs") + assert "Test from Python" in machine.succeed("mc cat rustfs/test-bucket/test.txt") + machine.succeed("mc rb --force rustfs/test-bucket") + ''; +} diff --git a/pkgs/by-name/ru/rustfs/package.nix b/pkgs/by-name/ru/rustfs/package.nix index 1a973ddb5281..bd1f974cf4f6 100644 --- a/pkgs/by-name/ru/rustfs/package.nix +++ b/pkgs/by-name/ru/rustfs/package.nix @@ -9,6 +9,7 @@ rustPlatform, protobuf, cacert, + nixosTests, }: let @@ -90,6 +91,10 @@ rustPlatform.buildRustPackage rec { "--skip=two_embedded_servers_isolate_auth_and_data_planes" ]; + passthru.tests = { + inherit (nixosTests) rustfs; + }; + meta = { description = "S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph"; homepage = "https://github.com/rustfs/rustfs";