mirror of
https://github.com/NixOS/nix
synced 2025-07-07 14:21:48 +02:00
tests: Move NixOS tests to tests/nixos
This will allow contributors to find them more easily.
This commit is contained in:
parent
04de0dd0b4
commit
74026bb101
10 changed files with 7 additions and 7 deletions
68
tests/nixos/containers/containers.nix
Normal file
68
tests/nixos/containers/containers.nix
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Test whether we can run a NixOS container inside a Nix build using systemd-nspawn.
|
||||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
makeTest ({
|
||||
name = "containers";
|
||||
|
||||
nodes =
|
||||
{
|
||||
host =
|
||||
{ config, lib, pkgs, nodes, ... }:
|
||||
{ virtualisation.writableStore = true;
|
||||
virtualisation.diskSize = 2048;
|
||||
virtualisation.additionalPaths =
|
||||
[ pkgs.stdenvNoCC
|
||||
(import ./systemd-nspawn.nix { inherit nixpkgs; }).toplevel
|
||||
];
|
||||
virtualisation.memorySize = 4096;
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
nix.extraOptions =
|
||||
''
|
||||
extra-experimental-features = nix-command auto-allocate-uids cgroups
|
||||
extra-system-features = uid-range
|
||||
'';
|
||||
nix.nixPath = [ "nixpkgs=${nixpkgs}" ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
start_all()
|
||||
|
||||
host.succeed("nix --version >&2")
|
||||
|
||||
# Test that 'id' gives the expected result in various configurations.
|
||||
|
||||
# Existing UIDs, sandbox.
|
||||
host.succeed("nix build -v --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1")
|
||||
host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]")
|
||||
|
||||
# Existing UIDs, no sandbox.
|
||||
host.succeed("nix build -v --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2")
|
||||
host.succeed("[[ $(cat ./result) = 'uid=30001(nixbld1) gid=30000(nixbld) groups=30000(nixbld)' ]]")
|
||||
|
||||
# Auto-allocated UIDs, sandbox.
|
||||
host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3")
|
||||
host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]")
|
||||
|
||||
# Auto-allocated UIDs, no sandbox.
|
||||
host.succeed("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4")
|
||||
host.succeed("[[ $(cat ./result) = 'uid=872415232 gid=30000(nixbld) groups=30000(nixbld)' ]]")
|
||||
|
||||
# Auto-allocated UIDs, UID range, sandbox.
|
||||
host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true")
|
||||
host.succeed("[[ $(cat ./result) = 'uid=0(root) gid=0(root) groups=0(root)' ]]")
|
||||
|
||||
# Auto-allocated UIDs, UID range, no sandbox.
|
||||
host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true")
|
||||
|
||||
# Run systemd-nspawn in a Nix build.
|
||||
host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}")
|
||||
host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]")
|
||||
'';
|
||||
|
||||
})
|
8
tests/nixos/containers/id-test.nix
Normal file
8
tests/nixos/containers/id-test.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{ name, uidRange ? false }:
|
||||
|
||||
with import <nixpkgs> {};
|
||||
|
||||
runCommand name
|
||||
{ requiredSystemFeatures = if uidRange then ["uid-range"] else [];
|
||||
}
|
||||
"id; id > $out"
|
78
tests/nixos/containers/systemd-nspawn.nix
Normal file
78
tests/nixos/containers/systemd-nspawn.nix
Normal file
|
@ -0,0 +1,78 @@
|
|||
{ nixpkgs }:
|
||||
|
||||
let
|
||||
|
||||
machine = { config, pkgs, ... }:
|
||||
{
|
||||
system.stateVersion = "22.05";
|
||||
boot.isContainer = true;
|
||||
systemd.services.console-getty.enable = false;
|
||||
networking.dhcpcd.enable = false;
|
||||
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
adminAddr = "nixos@example.org";
|
||||
};
|
||||
|
||||
systemd.services.test = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "httpd.service" ];
|
||||
script = ''
|
||||
source /.env
|
||||
echo "Hello World" > $out/msg
|
||||
ls -lR /dev > $out/dev
|
||||
${pkgs.curl}/bin/curl -sS --fail http://localhost/ > $out/page.html
|
||||
'';
|
||||
unitConfig = {
|
||||
FailureAction = "exit-force";
|
||||
FailureActionExitStatus = 42;
|
||||
SuccessAction = "exit-force";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
cfg = (import (nixpkgs + "/nixos/lib/eval-config.nix") {
|
||||
modules = [ machine ];
|
||||
system = "x86_64-linux";
|
||||
});
|
||||
|
||||
config = cfg.config;
|
||||
|
||||
in
|
||||
|
||||
with cfg._module.args.pkgs;
|
||||
|
||||
runCommand "test"
|
||||
{ buildInputs = [ config.system.path ];
|
||||
requiredSystemFeatures = [ "uid-range" ];
|
||||
toplevel = config.system.build.toplevel;
|
||||
}
|
||||
''
|
||||
root=$(pwd)/root
|
||||
mkdir -p $root $root/etc
|
||||
|
||||
export > $root/.env
|
||||
|
||||
# Make /run a tmpfs to shut up a systemd warning.
|
||||
mkdir /run
|
||||
mount -t tmpfs none /run
|
||||
chmod 0700 /run
|
||||
|
||||
mount -t cgroup2 none /sys/fs/cgroup
|
||||
|
||||
mkdir -p $out
|
||||
|
||||
touch /etc/os-release
|
||||
echo a5ea3f98dedc0278b6f3cc8c37eeaeac > /etc/machine-id
|
||||
|
||||
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1 \
|
||||
${config.systemd.package}/bin/systemd-nspawn \
|
||||
--keep-unit \
|
||||
-M ${config.networking.hostName} -D "$root" \
|
||||
--register=no \
|
||||
--resolv-conf=off \
|
||||
--bind-ro=/nix/store \
|
||||
--bind=$out \
|
||||
--private-network \
|
||||
$toplevel/init
|
||||
''
|
210
tests/nixos/github-flakes.nix
Normal file
210
tests/nixos/github-flakes.nix
Normal file
|
@ -0,0 +1,210 @@
|
|||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
let
|
||||
|
||||
# Generate a fake root CA and a fake api.github.com / github.com / channels.nixos.org certificate.
|
||||
cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; }
|
||||
''
|
||||
mkdir -p $out
|
||||
|
||||
openssl genrsa -out ca.key 2048
|
||||
openssl req -new -x509 -days 36500 -key ca.key \
|
||||
-subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt
|
||||
|
||||
openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \
|
||||
-subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=github.com" -out server.csr
|
||||
openssl x509 -req -extfile <(printf "subjectAltName=DNS:api.github.com,DNS:github.com,DNS:channels.nixos.org") \
|
||||
-days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt
|
||||
'';
|
||||
|
||||
registry = pkgs.writeTextFile {
|
||||
name = "registry";
|
||||
text = ''
|
||||
{
|
||||
"flakes": [
|
||||
{
|
||||
"from": {
|
||||
"type": "indirect",
|
||||
"id": "nixpkgs"
|
||||
},
|
||||
"to": {
|
||||
"type": "github",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": {
|
||||
"type": "indirect",
|
||||
"id": "private-flake"
|
||||
},
|
||||
"to": {
|
||||
"type": "github",
|
||||
"owner": "fancy-enterprise",
|
||||
"repo": "private-flake"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 2
|
||||
}
|
||||
'';
|
||||
destination = "/flake-registry.json";
|
||||
};
|
||||
|
||||
private-flake-rev = "9f1dd0df5b54a7dc75b618034482ed42ce34383d";
|
||||
|
||||
private-flake-api = pkgs.runCommand "private-flake" {}
|
||||
''
|
||||
mkdir -p $out/{commits,tarball}
|
||||
|
||||
# Setup https://docs.github.com/en/rest/commits/commits#get-a-commit
|
||||
echo '{"sha": "${private-flake-rev}"}' > $out/commits/HEAD
|
||||
|
||||
# Setup tarball download via API
|
||||
dir=private-flake
|
||||
mkdir $dir
|
||||
echo '{ outputs = {...}: {}; }' > $dir/flake.nix
|
||||
tar cfz $out/tarball/${private-flake-rev} $dir --hard-dereference
|
||||
'';
|
||||
|
||||
nixpkgs-api = pkgs.runCommand "nixpkgs-flake" {}
|
||||
''
|
||||
mkdir -p $out/commits
|
||||
|
||||
# Setup https://docs.github.com/en/rest/commits/commits#get-a-commit
|
||||
echo '{"sha": "${nixpkgs.rev}"}' > $out/commits/HEAD
|
||||
'';
|
||||
|
||||
archive = pkgs.runCommand "nixpkgs-flake" {}
|
||||
''
|
||||
mkdir -p $out/archive
|
||||
|
||||
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
|
||||
cp -prd ${nixpkgs} $dir
|
||||
# Set the correct timestamp in the tarball.
|
||||
find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
||||
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
||||
'';
|
||||
in
|
||||
|
||||
makeTest (
|
||||
|
||||
{
|
||||
name = "github-flakes";
|
||||
|
||||
nodes =
|
||||
{
|
||||
github =
|
||||
{ config, pkgs, ... }:
|
||||
{ networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
|
||||
services.httpd.enable = true;
|
||||
services.httpd.adminAddr = "foo@example.org";
|
||||
services.httpd.extraConfig = ''
|
||||
ErrorLog syslog:local6
|
||||
'';
|
||||
services.httpd.virtualHosts."channels.nixos.org" =
|
||||
{ forceSSL = true;
|
||||
sslServerKey = "${cert}/server.key";
|
||||
sslServerCert = "${cert}/server.crt";
|
||||
servedDirs =
|
||||
[ { urlPath = "/";
|
||||
dir = registry;
|
||||
}
|
||||
];
|
||||
};
|
||||
services.httpd.virtualHosts."api.github.com" =
|
||||
{ forceSSL = true;
|
||||
sslServerKey = "${cert}/server.key";
|
||||
sslServerCert = "${cert}/server.crt";
|
||||
servedDirs =
|
||||
[ { urlPath = "/repos/NixOS/nixpkgs";
|
||||
dir = nixpkgs-api;
|
||||
}
|
||||
{ urlPath = "/repos/fancy-enterprise/private-flake";
|
||||
dir = private-flake-api;
|
||||
}
|
||||
];
|
||||
};
|
||||
services.httpd.virtualHosts."github.com" =
|
||||
{ forceSSL = true;
|
||||
sslServerKey = "${cert}/server.key";
|
||||
sslServerCert = "${cert}/server.crt";
|
||||
servedDirs =
|
||||
[ { urlPath = "/NixOS/nixpkgs";
|
||||
dir = archive;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
client =
|
||||
{ config, lib, pkgs, nodes, ... }:
|
||||
{ virtualisation.writableStore = true;
|
||||
virtualisation.diskSize = 2048;
|
||||
virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ];
|
||||
virtualisation.memorySize = 4096;
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
nix.extraOptions = "experimental-features = nix-command flakes";
|
||||
networking.hosts.${(builtins.head nodes.github.config.networking.interfaces.eth1.ipv4.addresses).address} =
|
||||
[ "channels.nixos.org" "api.github.com" "github.com" ];
|
||||
security.pki.certificateFiles = [ "${cert}/ca.crt" ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
import json
|
||||
import time
|
||||
|
||||
start_all()
|
||||
|
||||
def cat_log():
|
||||
github.succeed("cat /var/log/httpd/*.log >&2")
|
||||
|
||||
github.wait_for_unit("httpd.service")
|
||||
|
||||
client.succeed("curl -v https://github.com/ >&2")
|
||||
out = client.succeed("nix registry list")
|
||||
print(out)
|
||||
assert "github:NixOS/nixpkgs" in out, "nixpkgs flake not found"
|
||||
assert "github:fancy-enterprise/private-flake" in out, "private flake not found"
|
||||
cat_log()
|
||||
|
||||
# If no github access token is provided, nix should use the public archive url...
|
||||
out = client.succeed("nix flake metadata nixpkgs --json")
|
||||
print(out)
|
||||
info = json.loads(out)
|
||||
assert info["revision"] == "${nixpkgs.rev}", f"revision mismatch: {info['revision']} != ${nixpkgs.rev}"
|
||||
cat_log()
|
||||
|
||||
# ... otherwise it should use the API
|
||||
out = client.succeed("nix flake metadata private-flake --json --access-tokens github.com=ghp_000000000000000000000000000000000000 --tarball-ttl 0")
|
||||
print(out)
|
||||
info = json.loads(out)
|
||||
assert info["revision"] == "${private-flake-rev}", f"revision mismatch: {info['revision']} != ${private-flake-rev}"
|
||||
cat_log()
|
||||
|
||||
client.succeed("nix registry pin nixpkgs")
|
||||
client.succeed("nix flake metadata nixpkgs --tarball-ttl 0 >&2")
|
||||
|
||||
# Shut down the web server. The flake should be cached on the client.
|
||||
github.succeed("systemctl stop httpd.service")
|
||||
|
||||
info = json.loads(client.succeed("nix flake metadata nixpkgs --json"))
|
||||
date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified']))
|
||||
assert date == "${nixpkgs.lastModifiedDate}", "time mismatch"
|
||||
|
||||
client.succeed("nix build nixpkgs#hello")
|
||||
|
||||
# The build shouldn't fail even with --tarball-ttl 0 (the server
|
||||
# being down should not be a fatal error).
|
||||
client.succeed("nix build nixpkgs#fuse --tarball-ttl 0")
|
||||
'';
|
||||
|
||||
})
|
77
tests/nixos/nix-copy-closure.nix
Normal file
77
tests/nixos/nix-copy-closure.nix
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Test ‘nix-copy-closure’.
|
||||
|
||||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; pkgD = pkgs.tmux; in {
|
||||
name = "nix-copy-closure";
|
||||
|
||||
nodes =
|
||||
{ client =
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ virtualisation.writableStore = true;
|
||||
virtualisation.additionalPaths = [ pkgA pkgD.drvPath ];
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
};
|
||||
|
||||
server =
|
||||
{ config, pkgs, ... }:
|
||||
{ services.openssh.enable = true;
|
||||
virtualisation.writableStore = true;
|
||||
virtualisation.additionalPaths = [ pkgB pkgC ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
import subprocess
|
||||
|
||||
start_all()
|
||||
|
||||
# Create an SSH key on the client.
|
||||
subprocess.run([
|
||||
"${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
|
||||
], capture_output=True, check=True)
|
||||
|
||||
client.succeed("mkdir -m 700 /root/.ssh")
|
||||
client.copy_from_host("key", "/root/.ssh/id_ed25519")
|
||||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the server.
|
||||
server.succeed("mkdir -m 700 /root/.ssh")
|
||||
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
server.wait_for_unit("sshd")
|
||||
client.wait_for_unit("network.target")
|
||||
client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'")
|
||||
|
||||
# Copy the closure of package A from the client to the server.
|
||||
server.fail("nix-store --check-validity ${pkgA}")
|
||||
client.succeed("nix-copy-closure --to server --gzip ${pkgA} >&2")
|
||||
server.succeed("nix-store --check-validity ${pkgA}")
|
||||
|
||||
# Copy the closure of package B from the server to the client.
|
||||
client.fail("nix-store --check-validity ${pkgB}")
|
||||
client.succeed("nix-copy-closure --from server --gzip ${pkgB} >&2")
|
||||
client.succeed("nix-store --check-validity ${pkgB}")
|
||||
|
||||
# Copy the closure of package C via the SSH substituter.
|
||||
client.fail("nix-store -r ${pkgC}")
|
||||
|
||||
# Copy the derivation of package D's derivation from the client to the server.
|
||||
server.fail("nix-store --check-validity ${pkgD.drvPath}")
|
||||
client.succeed("nix-copy-closure --to server --gzip ${pkgD.drvPath} >&2")
|
||||
server.succeed("nix-store --check-validity ${pkgD.drvPath}")
|
||||
|
||||
# FIXME
|
||||
# client.succeed(
|
||||
# "nix-store --option use-ssh-substituter true"
|
||||
# " --option ssh-substituter-hosts root\@server"
|
||||
# " -r ${pkgC} >&2"
|
||||
# )
|
||||
# client.succeed("nix-store --check-validity ${pkgC}")
|
||||
'';
|
||||
})
|
125
tests/nixos/nss-preload.nix
Normal file
125
tests/nixos/nss-preload.nix
Normal file
|
@ -0,0 +1,125 @@
|
|||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
let
|
||||
nix-fetch = pkgs.writeText "fetch.nix" ''
|
||||
derivation {
|
||||
# This derivation is an copy from what is available over at
|
||||
# nix.git:corepkgs/fetchurl.nix
|
||||
builder = "builtin:fetchurl";
|
||||
|
||||
# We're going to fetch data from the http_dns instance created before
|
||||
# we expect the content to be the same as the content available there.
|
||||
# ```
|
||||
# $ nix-hash --type sha256 --to-base32 $(echo "hello world" | sha256sum | cut -d " " -f 1)
|
||||
# 0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59
|
||||
# ```
|
||||
outputHash = "0ix4jahrkll5zg01wandq78jw3ab30q4nscph67rniqg5x7r0j59";
|
||||
outputHashAlgo = "sha256";
|
||||
outputHashMode = "flat";
|
||||
|
||||
name = "example.com";
|
||||
url = "http://example.com";
|
||||
|
||||
unpack = false;
|
||||
executable = false;
|
||||
|
||||
system = "builtin";
|
||||
|
||||
preferLocalBuild = true;
|
||||
|
||||
impureEnvVars = [
|
||||
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
|
||||
];
|
||||
|
||||
urls = [ "http://example.com" ];
|
||||
}
|
||||
'';
|
||||
in
|
||||
|
||||
makeTest (
|
||||
|
||||
rec {
|
||||
name = "nss-preload";
|
||||
|
||||
nodes = {
|
||||
http_dns = { lib, pkgs, config, ... }: {
|
||||
networking.firewall.enable = false;
|
||||
networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
|
||||
{ address = "fd21::1"; prefixLength = 64; }
|
||||
];
|
||||
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
|
||||
{ address = "192.168.0.1"; prefixLength = 24; }
|
||||
];
|
||||
|
||||
services.unbound = {
|
||||
enable = true;
|
||||
enableRootTrustAnchor = false;
|
||||
settings = {
|
||||
server = {
|
||||
interface = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ];
|
||||
access-control = [ "192.168.0.0/24 allow" "fd21::/64 allow" "::1 allow" "127.0.0.0/8 allow" ];
|
||||
local-data = [
|
||||
''"example.com. IN A 192.168.0.1"''
|
||||
''"example.com. IN AAAA fd21::1"''
|
||||
''"tarballs.nixos.org. IN A 192.168.0.1"''
|
||||
''"tarballs.nixos.org. IN AAAA fd21::1"''
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts."example.com" = {
|
||||
root = pkgs.runCommand "testdir" {} ''
|
||||
mkdir "$out"
|
||||
echo hello world > "$out/index.html"
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# client consumes a remote resolver
|
||||
client = { lib, nodes, pkgs, ... }: {
|
||||
networking.useDHCP = false;
|
||||
networking.nameservers = [
|
||||
(lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv6.addresses).address
|
||||
(lib.head nodes.http_dns.config.networking.interfaces.eth1.ipv4.addresses).address
|
||||
];
|
||||
networking.interfaces.eth1.ipv6.addresses = [
|
||||
{ address = "fd21::10"; prefixLength = 64; }
|
||||
];
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{ address = "192.168.0.10"; prefixLength = 24; }
|
||||
];
|
||||
|
||||
nix.settings.extra-sandbox-paths = lib.mkForce [];
|
||||
nix.settings.substituters = lib.mkForce [];
|
||||
nix.settings.sandbox = lib.mkForce true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: ''
|
||||
http_dns.wait_for_unit("nginx")
|
||||
http_dns.wait_for_open_port(80)
|
||||
http_dns.wait_for_unit("unbound")
|
||||
http_dns.wait_for_open_port(53)
|
||||
|
||||
client.start()
|
||||
client.wait_for_unit('multi-user.target')
|
||||
|
||||
with subtest("can fetch data from a remote server outside sandbox"):
|
||||
client.succeed("nix --version >&2")
|
||||
client.succeed("curl -vvv http://example.com/index.html >&2")
|
||||
|
||||
with subtest("nix-build can lookup dns and fetch data"):
|
||||
client.succeed("""
|
||||
nix-build ${nix-fetch} >&2
|
||||
""")
|
||||
'';
|
||||
})
|
112
tests/nixos/remote-builds.nix
Normal file
112
tests/nixos/remote-builds.nix
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Test Nix's remote build feature.
|
||||
|
||||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
makeTest (
|
||||
|
||||
let
|
||||
|
||||
# The configuration of the remote builders.
|
||||
builder =
|
||||
{ config, pkgs, ... }:
|
||||
{ services.openssh.enable = true;
|
||||
virtualisation.writableStore = true;
|
||||
nix.settings.sandbox = true;
|
||||
};
|
||||
|
||||
# Trivial Nix expression to build remotely.
|
||||
expr = config: nr: pkgs.writeText "expr.nix"
|
||||
''
|
||||
let utils = builtins.storePath ${config.system.build.extraUtils}; in
|
||||
derivation {
|
||||
name = "hello-${toString nr}";
|
||||
system = "i686-linux";
|
||||
PATH = "''${utils}/bin";
|
||||
builder = "''${utils}/bin/sh";
|
||||
args = [ "-c" "if [ ${toString nr} = 5 ]; then echo FAIL; exit 1; fi; echo Hello; mkdir $out $foo; cat /proc/sys/kernel/hostname > $out/host; ln -s $out $foo/bar; sleep 10" ];
|
||||
outputs = [ "out" "foo" ];
|
||||
}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
name = "remote-builds";
|
||||
|
||||
nodes =
|
||||
{ builder1 = builder;
|
||||
builder2 = builder;
|
||||
|
||||
client =
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ nix.settings.max-jobs = 0; # force remote building
|
||||
nix.distributedBuilds = true;
|
||||
nix.buildMachines =
|
||||
[ { hostName = "builder1";
|
||||
sshUser = "root";
|
||||
sshKey = "/root/.ssh/id_ed25519";
|
||||
system = "i686-linux";
|
||||
maxJobs = 1;
|
||||
}
|
||||
{ hostName = "builder2";
|
||||
sshUser = "root";
|
||||
sshKey = "/root/.ssh/id_ed25519";
|
||||
system = "i686-linux";
|
||||
maxJobs = 1;
|
||||
}
|
||||
];
|
||||
virtualisation.writableStore = true;
|
||||
virtualisation.additionalPaths = [ config.system.build.extraUtils ];
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
programs.ssh.extraConfig = "ConnectTimeout 30";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
import subprocess
|
||||
|
||||
start_all()
|
||||
|
||||
# Create an SSH key on the client.
|
||||
subprocess.run([
|
||||
"${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
|
||||
], capture_output=True, check=True)
|
||||
client.succeed("mkdir -p -m 700 /root/.ssh")
|
||||
client.copy_from_host("key", "/root/.ssh/id_ed25519")
|
||||
client.succeed("chmod 600 /root/.ssh/id_ed25519")
|
||||
|
||||
# Install the SSH key on the builders.
|
||||
client.wait_for_unit("network.target")
|
||||
for builder in [builder1, builder2]:
|
||||
builder.succeed("mkdir -p -m 700 /root/.ssh")
|
||||
builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
|
||||
builder.wait_for_unit("sshd")
|
||||
client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'")
|
||||
|
||||
# Perform a build and check that it was performed on the builder.
|
||||
out = client.succeed(
|
||||
"nix-build ${expr nodes.client.config 1} 2> build-output",
|
||||
"grep -q Hello build-output"
|
||||
)
|
||||
builder1.succeed(f"test -e {out}")
|
||||
|
||||
# And a parallel build.
|
||||
paths = client.succeed(r'nix-store -r $(nix-instantiate ${expr nodes.client.config 2})\!out $(nix-instantiate ${expr nodes.client.config 3})\!out')
|
||||
out1, out2 = paths.split()
|
||||
builder1.succeed(f"test -e {out1} -o -e {out2}")
|
||||
builder2.succeed(f"test -e {out1} -o -e {out2}")
|
||||
|
||||
# And a failing build.
|
||||
client.fail("nix-build ${expr nodes.client.config 5}")
|
||||
|
||||
# Test whether the build hook automatically skips unavailable builders.
|
||||
builder1.block()
|
||||
client.succeed("nix-build ${expr nodes.client.config 4}")
|
||||
'';
|
||||
})
|
126
tests/nixos/setuid.nix
Normal file
126
tests/nixos/setuid.nix
Normal file
|
@ -0,0 +1,126 @@
|
|||
# Verify that Linux builds cannot create setuid or setgid binaries.
|
||||
|
||||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
|
||||
inherit system;
|
||||
extraConfigurations = [ { nixpkgs.overlays = [ overlay ]; } ];
|
||||
};
|
||||
|
||||
makeTest {
|
||||
name = "setuid";
|
||||
|
||||
nodes.machine =
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ virtualisation.writableStore = true;
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
|
||||
virtualisation.additionalPaths = [ pkgs.stdenvNoCC pkgs.pkgsi686Linux.stdenvNoCC ];
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
start_all()
|
||||
|
||||
# Copying to /tmp should succeed.
|
||||
machine.succeed(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
# Creating a setuid binary should fail.
|
||||
machine.fail(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
chmod 4755 /tmp/id
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
# Creating a setgid binary should fail.
|
||||
machine.fail(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
chmod 2755 /tmp/id
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
# The checks should also work on 32-bit binaries.
|
||||
machine.fail(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> { system = "i686-linux"; }; runCommand "foo" {} "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
chmod 2755 /tmp/id
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
# The tests above use fchmodat(). Test chmod() as well.
|
||||
machine.succeed(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
perl -e \"chmod 0666, qw(/tmp/id) or die\"
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 666 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
machine.fail(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
perl -e \"chmod 04755, qw(/tmp/id) or die\"
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
# And test fchmod().
|
||||
machine.succeed(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\"
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
|
||||
machine.fail(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
|
||||
mkdir -p $out
|
||||
cp ${pkgs.coreutils}/bin/id /tmp/id
|
||||
perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\"
|
||||
")'
|
||||
""".strip())
|
||||
|
||||
machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
|
||||
|
||||
machine.succeed("rm /tmp/id")
|
||||
'';
|
||||
}
|
167
tests/nixos/sourcehut-flakes.nix
Normal file
167
tests/nixos/sourcehut-flakes.nix
Normal file
|
@ -0,0 +1,167 @@
|
|||
{ nixpkgs, system, overlay }:
|
||||
|
||||
with import (nixpkgs + "/nixos/lib/testing-python.nix")
|
||||
{
|
||||
inherit system;
|
||||
extraConfigurations = [{ nixpkgs.overlays = [ overlay ]; }];
|
||||
};
|
||||
|
||||
let
|
||||
# Generate a fake root CA and a fake git.sr.ht certificate.
|
||||
cert = pkgs.runCommand "cert" { buildInputs = [ pkgs.openssl ]; }
|
||||
''
|
||||
mkdir -p $out
|
||||
|
||||
openssl genrsa -out ca.key 2048
|
||||
openssl req -new -x509 -days 36500 -key ca.key \
|
||||
-subj "/C=NL/ST=Denial/L=Springfield/O=Dis/CN=Root CA" -out $out/ca.crt
|
||||
|
||||
openssl req -newkey rsa:2048 -nodes -keyout $out/server.key \
|
||||
-subj "/C=CN/ST=Denial/L=Springfield/O=Dis/CN=git.sr.ht" -out server.csr
|
||||
openssl x509 -req -extfile <(printf "subjectAltName=DNS:git.sr.ht") \
|
||||
-days 36500 -in server.csr -CA $out/ca.crt -CAkey ca.key -CAcreateserial -out $out/server.crt
|
||||
'';
|
||||
|
||||
registry = pkgs.writeTextFile {
|
||||
name = "registry";
|
||||
text = ''
|
||||
{
|
||||
"flakes": [
|
||||
{
|
||||
"from": {
|
||||
"type": "indirect",
|
||||
"id": "nixpkgs"
|
||||
},
|
||||
"to": {
|
||||
"type": "sourcehut",
|
||||
"owner": "~NixOS",
|
||||
"repo": "nixpkgs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 2
|
||||
}
|
||||
'';
|
||||
destination = "/flake-registry.json";
|
||||
};
|
||||
|
||||
nixpkgs-repo = pkgs.runCommand "nixpkgs-flake" { }
|
||||
''
|
||||
dir=NixOS-nixpkgs-${nixpkgs.shortRev}
|
||||
cp -prd ${nixpkgs} $dir
|
||||
|
||||
# Set the correct timestamp in the tarball.
|
||||
find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
|
||||
|
||||
mkdir -p $out/archive
|
||||
tar cfz $out/archive/${nixpkgs.rev}.tar.gz $dir --hard-dereference
|
||||
|
||||
echo 'ref: refs/heads/master' > $out/HEAD
|
||||
|
||||
mkdir -p $out/info
|
||||
echo -e '${nixpkgs.rev}\trefs/heads/master\n${nixpkgs.rev}\trefs/tags/foo-bar' > $out/info/refs
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
makeTest (
|
||||
|
||||
{
|
||||
name = "sourcehut-flakes";
|
||||
|
||||
nodes =
|
||||
{
|
||||
# Impersonate git.sr.ht
|
||||
sourcehut =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
|
||||
services.httpd.enable = true;
|
||||
services.httpd.adminAddr = "foo@example.org";
|
||||
services.httpd.extraConfig = ''
|
||||
ErrorLog syslog:local6
|
||||
'';
|
||||
services.httpd.virtualHosts."git.sr.ht" =
|
||||
{
|
||||
forceSSL = true;
|
||||
sslServerKey = "${cert}/server.key";
|
||||
sslServerCert = "${cert}/server.crt";
|
||||
servedDirs =
|
||||
[
|
||||
{
|
||||
urlPath = "/~NixOS/nixpkgs";
|
||||
dir = nixpkgs-repo;
|
||||
}
|
||||
{
|
||||
urlPath = "/~NixOS/flake-registry/blob/master";
|
||||
dir = registry;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
client =
|
||||
{ config, lib, pkgs, nodes, ... }:
|
||||
{
|
||||
virtualisation.writableStore = true;
|
||||
virtualisation.diskSize = 2048;
|
||||
virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ];
|
||||
virtualisation.memorySize = 4096;
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
flake-registry = https://git.sr.ht/~NixOS/flake-registry/blob/master/flake-registry.json
|
||||
'';
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.hosts.${(builtins.head nodes.sourcehut.config.networking.interfaces.eth1.ipv4.addresses).address} =
|
||||
[ "git.sr.ht" ];
|
||||
security.pki.certificateFiles = [ "${cert}/ca.crt" ];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
import json
|
||||
import time
|
||||
|
||||
start_all()
|
||||
|
||||
sourcehut.wait_for_unit("httpd.service")
|
||||
|
||||
client.succeed("curl -v https://git.sr.ht/ >&2")
|
||||
client.succeed("nix registry list | grep nixpkgs")
|
||||
|
||||
# Test that it resolves HEAD
|
||||
rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs --json | jq -r .revision")
|
||||
assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
|
||||
# Test that it resolves branches
|
||||
rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/master --json | jq -r .revision")
|
||||
assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
|
||||
# Test that it resolves tags
|
||||
rev = client.succeed("nix flake info sourcehut:~NixOS/nixpkgs/foo-bar --json | jq -r .revision")
|
||||
assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
|
||||
|
||||
# Registry and pinning test
|
||||
rev = client.succeed("nix flake info nixpkgs --json | jq -r .revision")
|
||||
assert rev.strip() == "${nixpkgs.rev}", "revision mismatch"
|
||||
|
||||
client.succeed("nix registry pin nixpkgs")
|
||||
|
||||
client.succeed("nix flake info nixpkgs --tarball-ttl 0 >&2")
|
||||
|
||||
# Shut down the web server. The flake should be cached on the client.
|
||||
sourcehut.succeed("systemctl stop httpd.service")
|
||||
|
||||
info = json.loads(client.succeed("nix flake info nixpkgs --json"))
|
||||
date = time.strftime("%Y%m%d%H%M%S", time.gmtime(info['lastModified']))
|
||||
assert date == "${nixpkgs.lastModifiedDate}", "time mismatch"
|
||||
|
||||
client.succeed("nix build nixpkgs#hello")
|
||||
|
||||
# The build shouldn't fail even with --tarball-ttl 0 (the server
|
||||
# being down should not be a fatal error).
|
||||
client.succeed("nix build nixpkgs#fuse --tarball-ttl 0")
|
||||
'';
|
||||
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue