This commit is contained in:
2025-05-29 17:57:06 +02:00
parent 2c14192d95
commit 394b5f13f8
6 changed files with 172 additions and 195 deletions

18
.gitignore vendored
View File

@ -1,3 +1,9 @@
# Nix #
/result
# Direnv #
/.direnv/
# Python # # Python #
# Virtual Environment # Virtual Environment
/.venv/ /.venv/
@ -13,18 +19,6 @@ __pycache__/
/.pytest_cache/ /.pytest_cache/
/.mypy_cache/ /.mypy_cache/
# Nix #
# Build
/result
# MicroVM
/var.img
/control.socket
# Direnv #
/.direnv/
# Project specific files # # Project specific files #
config.json config.json
db.json db.json

26
flake.lock generated
View File

@ -27,11 +27,11 @@
"spectrum": "spectrum" "spectrum": "spectrum"
}, },
"locked": { "locked": {
"lastModified": 1735074045, "lastModified": 1748464257,
"narHash": "sha256-CeYsC8J2dNiV2FCQOxK1oZ/jNpOF2io7aCEFHmfi95U=", "narHash": "sha256-PdnQSE2vPfql9WEjunj2qQnDpuuvk7HH+4djgXJSwFs=",
"owner": "astro", "owner": "astro",
"repo": "microvm.nix", "repo": "microvm.nix",
"rev": "2ae08de8e8068b00193b9cfbc0acc9dfdda03181", "rev": "e238645b6f0447a2eb1d538d300d5049d4006f9f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -42,17 +42,17 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1733759999, "lastModified": 1748370509,
"narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=", "narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=",
"owner": "nixos", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56", "rev": "4faa5f5321320e49a78ae7848582f684d64783e9",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56",
"type": "github" "type": "github"
} }
}, },
@ -65,11 +65,11 @@
"spectrum": { "spectrum": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1733308308, "lastModified": 1746869549,
"narHash": "sha256-+RcbMAjSxV1wW5UpS9abIG1lFZC8bITPiFIKNnE7RLs=", "narHash": "sha256-BKZ/yZO/qeLKh9YqVkKB6wJiDQJAZNN5rk5NsMImsWs=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "80c9e9830d460c944c8f730065f18bb733bc7ee2", "rev": "d927e78530892ec8ed389e8fae5f38abee00ad87",
"revCount": 792, "revCount": 862,
"type": "git", "type": "git",
"url": "https://spectrum-os.org/git/spectrum" "url": "https://spectrum-os.org/git/spectrum"
}, },

216
flake.nix
View File

@ -2,7 +2,7 @@
description = "A webserver to create files for testing purposes"; description = "A webserver to create files for testing purposes";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs?rev=a73246e2eef4c6ed172979932bc80e1404ba2d56"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
microvm = { microvm = {
url = "github:astro/microvm.nix"; url = "github:astro/microvm.nix";
@ -10,163 +10,92 @@
}; };
}; };
outputs = { outputs =
self, {
nixpkgs, self,
... nixpkgs,
} @ inputs: let ...
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"]; }@inputs:
forAllSystems = nixpkgs.lib.genAttrs supportedSystems; let
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system}.extend overlay); supportedSystems = [ "x86_64-linux" ];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
overlay = final: prev: rec { pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
python3Packages = prev.python3Packages.overrideScope (pfinal: pprev: { in
packageNameToDrv = x: builtins.getAttr (cleanPythonPackageName x) final.python3Packages; {
# `nix build`
packages = forAllSystems (system: rec {
default = testdata;
testdata = pkgs.${system}.callPackage ./nix/package.nix { src = ./.; };
vm = self.nixosConfigurations.vm.config.microvm.declaredRunner;
}); });
cleanPythonPackageName = x: let # `nix develop`
cleanName = builtins.match "([a-z,A-Z,0-9,_,-]+).*" x; devShells = forAllSystems (system: rec {
in default = venv;
if cleanName != null
then builtins.elemAt cleanName 0
else builtins.warn "Could not determine package name from '${x}'" null;
};
pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml); venv = pkgs.${system}.mkShell {
shellHook = ''
if [ ! -d .venv/ ]; then
echo "Creating Virtual Environment..."
${pkgs.${system}.python3}/bin/python3 -m venv .venv
fi
buildDependencies = forAllSystems (system: builtins.map pkgs.${system}.python3Packages.packageNameToDrv pyproject.build-system.requires); alias activate='source .venv/bin/activate'
runtimeDependencies = forAllSystems (system: builtins.map pkgs.${system}.python3Packages.packageNameToDrv pyproject.project.dependencies);
optionalDependencies = forAllSystems (system: builtins.mapAttrs (name: value: builtins.map pkgs.${system}.python3Packages.packageNameToDrv value) pyproject.project.optional-dependencies);
in {
# `nix build`
packages = forAllSystems (system: let
buildTestdata = {skipCheck ? false}:
pkgs.${system}.python3Packages.buildPythonPackage {
pname = pyproject.project.name;
version = pyproject.project.version;
src = ./.;
pyproject = true; echo "Entering Virtual Environment..."
source .venv/bin/activate
build-system = buildDependencies.${system};
dependencies = runtimeDependencies.${system};
optional-dependencies = optionalDependencies.${system};
nativeCheckInputs = optionalDependencies.${system}.dev;
checkPhase = let
dev = builtins.map (x: x.pname) optionalDependencies.${system}.dev;
in ''
${
if builtins.elem "pytest" dev && !skipCheck
then "pytest tests"
else ""
}
${
if builtins.elem "mypy" dev && !skipCheck
then "mypy src"
else ""
}
${
if builtins.elem "pylint" dev && !skipCheck
then "pylint src"
else ""
}
''; '';
}; };
in rec { });
default = testdata;
testdata = buildTestdata {skipCheck = false;};
quick = buildTestdata {skipCheck = true;};
vm = self.nixosConfigurations.vm.config.microvm.declaredRunner;
});
# `nix fmt` # NixOS Module
formatter = forAllSystems (system: pkgs.${system}.alejandra); nixosModules.default = import ./nix/module.nix inputs;
# `nix develop` # NixOS definition for a microvm to test nixosModules
devShells = forAllSystems (system: rec { nixosConfigurations."vm" = nixpkgs.lib.nixosSystem {
default = venv; system = "x86_64-linux";
venv = pkgs.${system}.mkShell {
shellHook = ''
if [ ! -d .venv/ ]; then
echo "Creating Virtual Environment..."
${pkgs.${system}.python3}/bin/python3 -m venv .venv
fi
alias activate='source .venv/bin/activate'
echo "Entering Virtual Environment..."
source .venv/bin/activate
'';
};
});
# NixOS Module
nixosModules.default = import ./nix/module.nix inputs;
# nixos definition for a microvm to test nixosModules
nixosConfigurations = let
system = "x86_64-linux";
in {
vm = nixpkgs.lib.nixosSystem {
inherit system;
modules = [ modules = [
inputs.microvm.nixosModules.microvm inputs.microvm.nixosModules.microvm
({config, ...}: { (
system.stateVersion = config.system.nixos.version; { config, ... }:
{
services.getty.autologinUser = "root";
networking.hostName = "vm"; microvm = {
users.users.root.password = ""; hypervisor = "qemu";
microvm = { shares = [
# volumes = [ {
# { # Host's /nix/store will be picked up so that no squashfs/erofs will be built for it.
# mountPoint = "/var"; tag = "ro-store";
# image = "var.img"; source = "/nix/store";
# size = 256; mountPoint = "/nix/.ro-store";
# } }
# ]; ];
shares = [
{
# use proto = "virtiofs" for MicroVMs that are started by systemd
proto = "9p";
tag = "ro-store";
# a host's /nix/store will be picked up so that no
# squashfs/erofs will be built for it.
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
];
interfaces = [ interfaces = [
{ {
type = "user"; type = "user";
id = "qemu"; id = "qemu";
mac = "02:00:00:01:01:01"; # Locally administered have one of 2/6/A/E in the second nibble.
} mac = "02:00:00:01:01:01";
]; }
];
forwardPorts = [ forwardPorts = [
{ {
host.port = config.services.testdata.port; host.port = config.services.testdata.port;
guest.port = config.services.testdata.port; guest.port = config.services.testdata.port;
} }
]; ];
};
# "qemu" has 9p built-in! }
hypervisor = "qemu"; )
socket = "control.socket";
};
})
self.nixosModules.default self.nixosModules.default
rec { rec {
networking.firewall.allowedTCPPorts = [services.testdata.port]; networking.firewall.allowedTCPPorts = [ services.testdata.port ];
services.testdata = { services.testdata = {
enable = true; enable = true;
@ -174,11 +103,15 @@
port = 1234; port = 1234;
settings = { settings = {
keys = ["one" "two" "three"]; keys = [
"one"
"two"
"three"
];
max-size = "1GB"; max-size = "1GB";
max-data = "100GB"; max-data = "100GB";
buffer-size = "12MiB"; buffer-size = "12MiB";
database = "/root/testdata_state.json"; database = "/root/testdata-state.json";
database-update-interval = 5.0; database-update-interval = 5.0;
log = "/root/log.jsonl"; log = "/root/log.jsonl";
}; };
@ -187,5 +120,4 @@
]; ];
}; };
}; };
};
} }

View File

@ -1,32 +1,41 @@
inputs: { inputs:
{
config, config,
lib, lib,
pkgs, pkgs,
... ...
}: let }:
let
cfg = config.services.testdata; cfg = config.services.testdata;
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default; package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default;
inherit (lib) mkIf mkEnableOption mkOption types; inherit (lib)
mkIf
mkEnableOption
mkOption
types
;
format = pkgs.formats.json {}; format = pkgs.formats.json { };
configFile = format.generate "config.json" cfg.settings; configFile = format.generate "config.json" cfg.settings;
in { in
{
options.services.testdata = { options.services.testdata = {
enable = mkEnableOption "testdata"; enable = mkEnableOption "testdata";
settings = mkOption { settings = mkOption {
type = with types; let type =
valueType = nullOr (oneOf [ with types;
# TODO: restrict type to actual config file structure let
bool valueType = nullOr (oneOf [
int bool
float int
str float
path str
(attrsOf valueType) path
(listOf valueType) (attrsOf valueType)
]); (listOf valueType)
in ]);
in
valueType; valueType;
default = throw "Please specify services.testdata.settings"; default = throw "Please specify services.testdata.settings";
}; };
@ -43,7 +52,7 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [package]; environment.systemPackages = [ package ];
systemd.services.testdata = { systemd.services.testdata = {
enable = true; enable = true;
@ -53,7 +62,7 @@ in {
ExecStart = "${package}/bin/testdata --config ${configFile} --listen ${cfg.host} --port ${builtins.toString cfg.port}"; ExecStart = "${package}/bin/testdata --config ${configFile} --listen ${cfg.host} --port ${builtins.toString cfg.port}";
}; };
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
}; };
}; };
} }

46
nix/package.nix Normal file
View File

@ -0,0 +1,46 @@
{
src,
python3Packages,
}:
let
inherit (python3Packages)
setuptools
fastapi
uvicorn
pydantic
pytest
requests
mypy
pylint
;
project = (builtins.fromTOML (builtins.readFile "${src}/pyproject.toml")).project;
pname = project.name;
version = project.version;
in
python3Packages.buildPythonPackage {
inherit pname version src;
pyproject = true;
build-system = [ setuptools ];
dependencies = [
fastapi
uvicorn
pydantic
];
nativeCheckInputs = [
pytest
requests
mypy
pylint
];
checkPhase = ''
pytest tests
mypy src
pylint src
'';
}

View File

@ -1,12 +1,8 @@
[project] [project]
name = "testdata" name = "testdata"
version = "1.2.1" version = "1.2.2"
requires-python = "~=3.12, <4" requires-python = "~=3.12, <4"
dependencies = [ dependencies = ["fastapi~=0.115", "uvicorn~=0.32", "pydantic~=2.9"]
"fastapi~=0.115",
"uvicorn~=0.32",
"pydantic~=2.9",
]
[project.optional-dependencies] [project.optional-dependencies]
dev = [ dev = [
@ -14,14 +10,14 @@ dev = [
"mypy~=1.13", "mypy~=1.13",
"pylint~=3.3", "pylint~=3.3",
"requests~=2.32", "requests~=2.32",
"types-requests~=2.32" "types-requests~=2.32",
] ]
[project.scripts] [project.scripts]
testdata = "testdata.main:main" testdata = "testdata.main:main"
[build-system] [build-system]
requires = ["setuptools~=75.1"] requires = ["setuptools~=78.1"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
@ -40,5 +36,5 @@ disable = [
"missing-class-docstring", "missing-class-docstring",
"missing-function-docstring", "missing-function-docstring",
"too-few-public-methods", "too-few-public-methods",
"broad-exception-caught" "broad-exception-caught",
] ]