From 1f42f4b8881682abb3331845e02df2948eedfa91 Mon Sep 17 00:00:00 2001 From: Rory& Date: Mon, 19 Jan 2026 11:57:22 +0100 Subject: [PATCH] Nix test VM, migrations helper script --- default.nix | 1 + flake.nix | 1 + nix/modules/default/default.nix | 23 +++++++++ nix/testVm/configuration.nix | 45 ++++++++++++++++++ nix/testVm/default.nix | 15 ++++++ nix/testVm/perlless.nix | 26 +++++++++++ nix/testVm/postgres.nix | 82 +++++++++++++++++++++++++++++++++ nix/testVm/vm.nix | 68 +++++++++++++++++++++++++++ src/apply-migrations.ts | 15 ++++++ 9 files changed, 276 insertions(+) create mode 100644 nix/testVm/configuration.nix create mode 100644 nix/testVm/default.nix create mode 100644 nix/testVm/perlless.nix create mode 100644 nix/testVm/postgres.nix create mode 100644 nix/testVm/vm.nix create mode 100644 src/apply-migrations.ts diff --git a/default.nix b/default.nix index 62d9000ed..7763d61c7 100644 --- a/default.nix +++ b/default.nix @@ -77,6 +77,7 @@ pkgs.buildNpmPackage { do makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/$i done + makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/apply-migrations --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/dist/apply-migrations.js # set +x runHook postInstall diff --git a/flake.nix b/flake.nix index 3f8cb4219..ab482c6aa 100644 --- a/flake.nix +++ b/flake.nix @@ -76,6 +76,7 @@ ) // { nixosModules.default = import ./nix/modules/default self; + testVm = import ./nix/testVm/default.nix { inherit self nixpkgs; }; checks = let pkgs = import nixpkgs { system = "x86_64-linux"; }; diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index 30c15d59b..e3a65fb7f 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -221,8 +221,27 @@ in # } ]; + systemd.services.spacebar-apply-migrations = makeServerTsService { + description = "Spacebar Server - Apply DB migrations"; + after = [ "network-online.target" "postgresql.service" ]; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + cfg.extraEnvironment + // { + # things we force... + CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + } + ); + serviceConfig = { + ExecStart = "${cfg.package}/bin/apply-migrations"; + Type = "oneshot"; + }; + }; + systemd.services.spacebar-api = makeServerTsService { description = "Spacebar Server - API"; + after = [ "spacebar-apply-migrations.service" ]; + requires = [ "spacebar-apply-migrations.service" ]; environment = builtins.mapAttrs (_: val: builtins.toString val) ( { # things we set by default... @@ -245,6 +264,8 @@ in systemd.services.spacebar-gateway = makeServerTsService { description = "Spacebar Server - Gateway"; + after = [ "spacebar-apply-migrations.service" ]; + requires = [ "spacebar-apply-migrations.service" ]; environment = builtins.mapAttrs (_: val: builtins.toString val) ( { # things we set by default... @@ -267,6 +288,8 @@ in systemd.services.spacebar-cdn = makeServerTsService { description = "Spacebar Server - CDN"; + after = [ "spacebar-apply-migrations.service" ]; + requires = [ "spacebar-apply-migrations.service" ]; environment = builtins.mapAttrs (_: val: builtins.toString val) ( { # things we set by default... diff --git a/nix/testVm/configuration.nix b/nix/testVm/configuration.nix new file mode 100644 index 000000000..c826ca0a5 --- /dev/null +++ b/nix/testVm/configuration.nix @@ -0,0 +1,45 @@ +{ pkgs, lib, ... }: +{ + networking.hostName = "sbtest"; + + services.spacebarchat-server = + let + cfg = { + enable = true; + apiEndpoint = { useSsl = false; host = "api.sb.localhost"; localPort = 3001; publicPort = 8080; }; + gatewayEndpoint = { useSsl = false; host = "gw.sb.localhost"; localPort = 3002; publicPort = 8080; }; + cdnEndpoint = { useSsl = false; host = "cdn.sb.localhost"; localPort = 3003; publicPort = 8080; }; + nginx.enable = true; + serverName = "sb.localhost"; + extraEnvironment = { + DATABASE = "postgres://postgres:postgres@127.0.0.1/spacebar"; + #WEBRTC_PORT_RANGE=60000-61000; + #PUBLIC_IP=216.230.228.60; + LOG_REQUESTS = "-200,204,304"; + LOG_VALIDATION_ERRORS = true; + #DB_LOGGING=true; + #LOG_GATEWAY_TRACES=true; + #LOG_PROTO_UPDATES=true; + #LOG_PROTO_FRECENCY_UPDATES=true; + #LOG_PROTO_SETTINGS_UPDATES=true; + #WRTC_PUBLIC_IP=webrtc.old.server.spacebar.chat; + WRTC_PUBLIC_IP = "216.230.228.19"; + WRTC_PORT_MIN = 60000; + WRTC_PORT_MAX = 65000; + WRTC_LIBRARY = "@spacebarchat/medooze-webrtc"; + #WRTC_LIBRARY=mediasoup-spacebar-wrtc; + }; + }; + in + lib.trace ("Testing with config: " + builtins.toJSON cfg) cfg; + services.nginx.enable = true; + + users.users.root.initialPassword = "root"; + services.getty.autologinUser = "root"; + + environment.systemPackages = with pkgs; [ + btop + duf + lnav + ]; +} diff --git a/nix/testVm/default.nix b/nix/testVm/default.nix new file mode 100644 index 000000000..3b67f4927 --- /dev/null +++ b/nix/testVm/default.nix @@ -0,0 +1,15 @@ +{ self, nixpkgs }: + +nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + self.nixosModules.default + ./configuration.nix + ./postgres.nix + ./perlless.nix + ./vm.nix + ]; + specialArgs = { inherit self nixpkgs; }; +} + +#DERIVATION=".#nixosConfigurations.${CONFIG}.config.system.build.toplevel" \ No newline at end of file diff --git a/nix/testVm/perlless.nix b/nix/testVm/perlless.nix new file mode 100644 index 000000000..6d391569f --- /dev/null +++ b/nix/testVm/perlless.nix @@ -0,0 +1,26 @@ +{ pkgs, lib, ... }: +{ + #perlless profile +# system.switch.enable = lib.mkForce false; + + # Remove perl from activation + #system.etc.overlay.enable = lib.mkForce true; + #systemd.sysusers.enable = lib.mkForce true; + + # Random perl remnants + programs.less.lessopen = lib.mkForce null; + programs.command-not-found.enable = lib.mkForce false; + environment.defaultPackages = lib.mkForce [ ]; + documentation.info.enable = lib.mkForce false; + documentation.man.enable = false; + + system = { + #activatable = false; + copySystemConfiguration = false; + includeBuildDependencies = false; + disableInstallerTools = lib.mkForce true; + build = { + separateActivationScript = true; + }; + }; +} diff --git a/nix/testVm/postgres.nix b/nix/testVm/postgres.nix new file mode 100644 index 000000000..015f6d96b --- /dev/null +++ b/nix/testVm/postgres.nix @@ -0,0 +1,82 @@ +{ config, pkgs, ... }: + +{ + services.postgresql = { + enable = true; + package = pkgs.postgresql_18_jit; + initdbArgs = [ + "--encoding=UTF8" + "--locale=C.UTF-8" + "--data-checksums" + "--allow-group-access" + ]; + enableTCPIP = true; + authentication = pkgs.lib.mkOverride 10 '' + # TYPE, DATABASE, USER, ADDRESS, METHOD + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + host all all 0.0.0.0/0 md5 + ''; + initialScript = pkgs.writeText "backend-initScript" '' + CREATE ROLE spacebar WITH LOGIN PASSWORD 'spacebar' CREATEDB; + CREATE DATABASE spacebar; + GRANT ALL PRIVILEGES ON DATABASE spacebar TO spacebar; + ''; + settings = { + # https://pgconfigurator.cybertec.at/ + max_connections = 2500; + superuser_reserved_connections = 3; + + shared_buffers = "128MB"; + work_mem = "64MB"; + maintenance_work_mem = "512MB"; + huge_pages = "try"; + effective_cache_size = "512MB"; + effective_io_concurrency = 100; + random_page_cost = 1.1; + + # can use this to view stats: SELECT query, total_time, calls, rows FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10; + shared_preload_libraries = "pg_stat_statements"; + track_io_timing = "on"; + track_functions = "pl"; + "pg_stat_statements.max" = "10000"; # additional + "pg_stat_statements.track" = "all"; # additional + + wal_level = "replica"; + max_wal_senders = 0; + synchronous_commit = "on"; # was ond3 + + checkpoint_timeout = "15min"; + checkpoint_completion_target = "0.9"; + max_wal_size = "2GB"; + min_wal_size = "1GB"; + + wal_compression = "off"; + wal_buffers = "-1"; + wal_writer_delay = "500ms"; # was 100 + wal_writer_flush_after = "32MB"; # was 1 + #checkpoint_segments = "64"; # additional + default_statistics_target = "250"; # additional + + bgwriter_delay = "200ms"; + bgwriter_lru_maxpages = "100"; + bgwriter_lru_multiplier = "2.0"; + bgwriter_flush_after = "0"; + + max_worker_processes = "64"; # was 14 + max_parallel_workers_per_gather = "32"; # was 7 + max_parallel_maintenance_workers = "32"; # was 7 + max_parallel_workers = "64"; # was 14 + parallel_leader_participation = "on"; + + enable_partitionwise_join = "on"; + enable_partitionwise_aggregate = "on"; + jit = "on"; + max_slot_wal_keep_size = "1GB"; + track_wal_io_timing = "on"; + maintenance_io_concurrency = "4"; + wal_recycle = "on"; + }; + }; +} diff --git a/nix/testVm/vm.nix b/nix/testVm/vm.nix new file mode 100644 index 000000000..8a2821354 --- /dev/null +++ b/nix/testVm/vm.nix @@ -0,0 +1,68 @@ +{ + nixpkgs, + modulesPath, + pkgs, + lib, + ... +}: +{ + imports = [ + # (modulesPath + "/virtualisation/qemu-vm.nix") + ]; + virtualisation.vmVariant = { + services.xserver.videoDrivers = [ "qxl" ]; + services.spice-vdagentd.enable = true; + virtualisation.qemu.guestAgent.enable = true; + services.qemuGuest.enable = true; + virtualisation.qemu.options = [ + "-vga qxl -device virtio-serial-pci -spice port=5930,disable-ticketing=on -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 -chardev spicevmc,id=spicechannel0,name=vdagent" + "-display gtk,zoom-to-fit=off,show-cursor=on" + "-device virtio-balloon" + ]; + virtualisation.memorySize = 4096; + virtualisation.cores = 6; + virtualisation.forwardPorts = [ + # { hostPort = 2222; guestPort = 22; } # Probably shouldn't do this with root:root lol + { from = "host"; host.port = 8080; guest.port = 80; } + ]; + networking.useDHCP = lib.mkForce true; + }; + + networking.firewall.enable = false; + + boot = { + initrd = { + systemd.enable = true; + systemd.emergencyAccess = true; + }; + kernelParams = [ + "console=ttyS0,115200" + "systemd.gpt_auto=0" + #"console=tty1" + #"quiet" + ]; + loader.timeout = 1; + }; + boot.supportedFilesystems = lib.mkForce [ ]; + hardware.enableRedistributableFirmware = lib.mkForce false; + #environment.systemPackages = lib.mkForce [ ]; + documentation.enable = lib.mkForce false; + documentation.nixos.enable = lib.mkForce false; + networking.wireless.enable = lib.mkForce false; + + console = { + earlySetup = true; + font = "${pkgs.cozette}/share/consolefonts/cozette6x13.psfu"; + packages = with pkgs; [ cozette ]; + }; + + system = { + #activatable = false; + copySystemConfiguration = false; + includeBuildDependencies = false; + disableInstallerTools = lib.mkForce true; + build = { + separateActivationScript = true; + }; + }; +} diff --git a/src/apply-migrations.ts b/src/apply-migrations.ts new file mode 100644 index 000000000..5c8060240 --- /dev/null +++ b/src/apply-migrations.ts @@ -0,0 +1,15 @@ +import moduleAlias from "module-alias"; +moduleAlias(__dirname + "../../package.json"); +process.on("uncaughtException", console.error); +process.on("unhandledRejection", console.error); + +import { config } from "dotenv"; +config({ quiet: true }); + +process.env.DB_LOGGING = "true"; + +import { closeDatabase, initDatabase } from "@spacebar/util"; + +initDatabase().then(() => { + closeDatabase().then((r) => {}); +});