Add some basic testcases, fix the tools on multi-user Nix installations, fix pure-sysvinit examples

This commit is contained in:
Sander van der Burg 2020-07-06 22:06:51 +02:00 committed by Sander van der Burg
parent 7e518eabb8
commit 7897fc0b15
16 changed files with 405 additions and 29 deletions

View File

@ -19,6 +19,6 @@ let
in
{
webapp = import webappExpr {
inherit createManagedProcess runtimeDir;
inherit createManagedProcess tmpDir;
};
}

View File

@ -0,0 +1,13 @@
{ pkgs ? import <nixpkgs> { inherit system; }
, system ? builtins.currentSystem
, stateDir ? "/var"
, runtimeDir ? "${stateDir}/run"
, logDir ? "${stateDir}/log"
, cacheDir ? "${stateDir}/cache"
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
, forceDisableUserChange ? false
, processManager
}:
{
}

View File

@ -1,4 +1,4 @@
{createManagedProcess, runtimeDir}:
{createManagedProcess, tmpDir}:
{port, instanceSuffix ? ""}:
let
@ -16,7 +16,7 @@ createManagedProcess {
environment = {
PORT = port;
PID_FILE = "${runtimeDir}/${instanceName}.pid";
PID_FILE = "${tmpDir}/${instanceName}.pid";
};
user = instanceName;
credentials = {

View File

@ -1,4 +1,4 @@
{createManagedProcess, runtimeDir}:
{createManagedProcess, tmpDir}:
{port, instanceSuffix ? ""}:
let

View File

@ -1,4 +1,4 @@
{createManagedProcess, runtimeDir}:
{createManagedProcess, tmpDir}:
{port, instanceSuffix ? ""}:
let
@ -17,7 +17,7 @@ createManagedProcess {
environment = {
PORT = port;
PID_FILE = "${runtimeDir}/${instanceName}.pid";
PID_FILE = "${tmpDir}/${instanceName}.pid";
};
user = instanceName;
credentials = {

View File

@ -24,11 +24,11 @@ let
in
{
webapp = import ./webapp.nix {
inherit createSystemVInitScript runtimeDir;
inherit createSystemVInitScript tmpDir;
};
nginxReverseProxy = import ./nginx-reverse-proxy.nix {
inherit createSystemVInitScript stateDir logDir runtimeDir;
inherit createSystemVInitScript stateDir logDir runtimeDir forceDisableUserChange;
inherit (pkgs) stdenv writeTextFile nginx;
};
}

View File

@ -1,22 +1,32 @@
{createSystemVInitScript, stdenv, writeTextFile, nginx, runtimeDir, stateDir, logDir}:
{createSystemVInitScript, stdenv, writeTextFile, nginx, runtimeDir, stateDir, logDir, forceDisableUserChange}:
{port ? 80, webapps ? [], instanceSuffix ? ""}:
interDeps:
interDependencies:
let
instanceName = "nginx${instanceSuffix}";
user = instanceName;
group = instanceName;
nginxStateDir = "${stateDir}/${instanceName}";
dependencies = webapps ++ (builtins.attrValues interDeps);
nginxLogDir = "${nginxStateDir}/logs";
in
import ./nginx.nix {
inherit createSystemVInitScript nginx;
inherit createSystemVInitScript stdenv nginx forceDisableUserChange;
stateDir = nginxStateDir;
} {
inherit instanceSuffix;
dependencies = map (webapp: webapp.pkg) webapps
++ map (interDependency: interDependency.pkgs."${stdenv.system}") (builtins.attrValues interDependencies);
configFile = writeTextFile {
name = "nginx.conf";
text = ''
error_log ${nginxStateDir}/logs/error.log;
pid ${runtimeDir}/${instanceName}.pid;
error_log ${nginxLogDir}/error.log;
${stdenv.lib.optionalString (!forceDisableUserChange) ''
user ${user} ${group};
''}
events {
worker_connections 190000;
@ -29,15 +39,15 @@ import ./nginx.nix {
}
'') webapps}
${stdenv.lib.concatMapStrings (dependencyName:
${stdenv.lib.concatMapStrings (paramName:
let
dependency = builtins.getAttr dependencyName interDeps;
dependency = builtins.getAttr paramName interDependencies;
in
''
upstream webapp${toString dependency.port} {
server ${dependency.target.properties.hostname}:${toString dependency.port};
}
'') (builtins.attrNames interDeps)}
'') (builtins.attrNames interDependencies)}
# Fallback virtual host displaying an error page. This is what users see
# if they connect to a non-deployed web application.
@ -56,12 +66,11 @@ import ./nginx.nix {
server_name ${dependency.dnsName};
location / {
proxy_pass http://webapp${toString dependency.port};
proxy_pass http://webapp${toString dependency.port};
}
}
'') dependencies}
'') (webapps ++ builtins.attrValues interDependencies)}
}
'';
};
dependencies = map (webapp: webapp.pkg) dependencies;
}

View File

@ -1,17 +1,20 @@
{createSystemVInitScript, nginx, stateDir}:
{createSystemVInitScript, stdenv, nginx, stateDir, forceDisableUserChange}:
{configFile, dependencies ? [], instanceSuffix ? ""}:
let
instanceName = "nginx${instanceSuffix}";
user = instanceName;
group = instanceName;
nginxLogDir = "${stateDir}/${instanceName}/logs";
nginxLogDir = "${stateDir}/logs";
in
createSystemVInitScript {
name = instanceName;
description = "Nginx";
initialize = ''
mkdir -p ${nginxLogDir}
${stdenv.lib.optionalString (!forceDisableUserChange) ''
chown ${user}:${group} ${nginxLogDir}
''}
'';
process = "${nginx}/bin/nginx";
args = [ "-c" configFile "-p" stateDir ];

View File

@ -0,0 +1,11 @@
{ pkgs ? import <nixpkgs> { inherit system; }
, system ? builtins.currentSystem
, stateDir ? "/var"
, runtimeDir ? "${stateDir}/run"
, logDir ? "${stateDir}/log"
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
, forceDisableUserChange ? false
}:
{
}

View File

@ -1,4 +1,4 @@
{createSystemVInitScript, runtimeDir}:
{createSystemVInitScript, tmpDir}:
{port, instanceSuffix ? ""}:
let
@ -12,7 +12,7 @@ createSystemVInitScript {
args = [ "-D" ];
environment = {
PORT = port;
PID_FILE = "${runtimeDir}/${instanceName}.pid";
PID_FILE = "${tmpDir}/${instanceName}.pid";
};
runlevels = [ 3 4 5 ];

View File

@ -109,7 +109,7 @@ let
activity = "Starting";
instruction =
initialize +
(if processIsDaemon then "${startDaemon} ${stdenv.lib.optionalString (pidFile != null) "-f -p ${pidFile}"} ${stdenv.lib.optionalString (nice != null) "-n ${nice}"} ${stdenv.lib.optionalString (_user != null) "su ${_user} -c '"} ${process} ${stdenv.lib.escapeShellArgs args} ${stdenv.lib.optionalString (_user != null) "'"}"
(if processIsDaemon then "${startDaemon} ${stdenv.lib.optionalString (pidFile != null) "-f -p ${pidFile}"} ${stdenv.lib.optionalString (nice != null) "-n ${nice}"} ${stdenv.lib.optionalString (_user != null) "$(type -p su) ${_user} -c '"}${process} ${stdenv.lib.escapeShellArgs args} ${stdenv.lib.optionalString (_user != null) "'"}"
else "${startProcessAsDaemon} -U -i ${if pidFile == null then "-P ${runtimeDir} -n $(basename ${process})" else "-F ${pidFile}"} ${stdenv.lib.optionalString (_user != null) "-u ${_user}"} -- ${process} ${toString args}");
};
stop = {
@ -151,7 +151,7 @@ let
else defaultStop;
_environment = stdenv.lib.optionalAttrs (path != []) {
PATH = builtins.concatStringsSep ":" (map(package: "${package}/bin" ) path);
PATH = builtins.concatStringsSep ":" (map (package: "${package}/bin") path) + ":$PATH";
} // environment;
initdScript = writeTextFile {

View File

@ -1,7 +1,7 @@
{stdenv, fetchurl, basePackages, runtimeDir}:
let
basePath = builtins.concatStringsSep ":" (map (package: "${package}/bin") basePackages);
basePath = builtins.concatStringsSep ":" (map (package: "${package}/bin") basePackages) + ":\\$PATH";
src = fetchurl {
url = http://www.linuxfromscratch.org/lfs/downloads/9.0/lfs-bootscripts-20190524.tar.xz;

182
tests/webapps-agnostic.nix Normal file
View File

@ -0,0 +1,182 @@
{nixpkgs ? <nixpkgs>}:
with import "${nixpkgs}/nixos/lib/testing-python.nix" { system = builtins.currentSystem; };
let
webappUnprivilegedForegroundMode = (import ../examples/webapps-agnostic/processes.nix {
forceDisableUserChange = true;
stateDir = "/home/unprivileged/var";
processManager = "sysvinit";
webappMode = "foreground";
}).webapp.pkg;
webappUnprivilegedDaemonMode = (import ../examples/webapps-agnostic/processes.nix {
forceDisableUserChange = true;
stateDir = "/home/unprivileged/var";
processManager = "sysvinit";
webappMode = "daemon";
}).webapp.pkg;
webappUnprivilegedAutoMode = (import ../examples/webapps-agnostic/processes.nix {
forceDisableUserChange = true;
stateDir = "/home/unprivileged/var";
processManager = "sysvinit";
webappMode = null;
}).webapp.pkg;
processesEnv = import ../nixproc/create-managed-process/sysvinit/build-sysvinit-env.nix {
exprFile = ../examples/webapps-agnostic/processes.nix;
};
tools = import ../tools {};
nix-processmgmt = ./..;
env = "NIX_PATH=nixpkgs=${nixpkgs}";
in
makeTest {
machine =
{pkgs, ...}:
{
virtualisation.pathsInNixDB = [ pkgs.stdenv ] ++ pkgs.coreutils.all ++ [ webappUnprivilegedForegroundMode webappUnprivilegedDaemonMode webappUnprivilegedAutoMode processesEnv ];
virtualisation.writableStore = true;
users.extraUsers = {
unprivileged = {
uid = 1000;
group = "users";
shell = "/bin/sh";
description = "Unprivileged user";
home = "/home/unprivileged";
createHome = true;
};
};
# We can't download any substitutes in a test environment. To make tests
# faster, we disable substitutes so that Nix does not waste any time by
# attempting to download them.
nix.extraOptions = ''
substitute = false
'';
environment.systemPackages = [
pkgs.stdenv
pkgs.dysnomia
tools.build
tools.sysvinit
];
};
testScript = ''
def check_webapp_daemon(package):
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp start"'.format(package)
)
machine.succeed("pgrep -u unprivileged -f '/bin/webapp -D$'")
machine.succeed("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp stop"'.format(package)
)
machine.fail("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
def check_webapp_foreground(package):
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp start"'.format(package)
)
machine.succeed(
"sleep 1"
) # Wait a bit to ensure that the webapp is ready to accept connections
machine.succeed("pgrep -u unprivileged -f '/bin/webapp$'")
machine.succeed("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp stop"'.format(package)
)
machine.fail("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
def check_nginx_redirection():
machine.succeed(
"curl --fail -H 'Host: webapp.local' http://localhost:8080 | grep 'listening on port: 5000'"
)
def check_nginx_multi_instance_redirection():
machine.succeed(
"curl --fail -H 'Host: webapp1.local' http://localhost:8080 | grep 'listening on port: 5000'"
)
machine.succeed(
"curl --fail -H 'Host: webapp5.local' http://localhost:8081 | grep 'listening on port: 6002'"
)
def check_system_unavailable():
machine.fail("curl --fail http://localhost:8080")
machine.fail("pgrep -f '/bin/webapp'")
start_all()
# Make sure the unprivileged user can deploy
machine.succeed('su - unprivileged -c "mkdir -p var/run var/tmp"')
# Test webapp in foreground mode as an unprivileged user
check_webapp_foreground("${webappUnprivilegedForegroundMode}")
# Test webapp daemon as an unprivileged user
check_webapp_daemon("${webappUnprivilegedDaemonMode}")
# Test webapp auto mode by an unprivileged user
check_webapp_daemon("${webappUnprivilegedAutoMode}")
# Deploy the entire system as an unprivileged user
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-agnostic/processes.nix'"
)
check_nginx_redirection()
# Upgrade to the multi-instance example and check if the redirections are done right
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-agnostic/processes-advanced.nix'"
)
check_nginx_multi_instance_redirection()
# Undeploy the entire system as an unprivileged user
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-agnostic/processes-empty.nix'"
)
check_system_unavailable()
# Deploy the entire system
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-agnostic/processes.nix"
)
check_nginx_redirection()
# Upgrade to the multi-instance example and check if the redirections are done right
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-agnostic/processes-advanced.nix"
)
check_nginx_multi_instance_redirection()
# Undeploy the entire system
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-agnostic/processes-empty.nix"
)
check_system_unavailable()
'';
}

141
tests/webapps-sysvinit.nix Normal file
View File

@ -0,0 +1,141 @@
{nixpkgs ? <nixpkgs>}:
with import "${nixpkgs}/nixos/lib/testing-python.nix" { system = builtins.currentSystem; };
let
webappUnprivileged = (import ../examples/webapps-sysvinit/processes.nix {
forceDisableUserChange = true;
stateDir = "/home/unprivileged/var";
}).webapp.pkg;
processesEnv = import ../nixproc/create-managed-process/sysvinit/build-sysvinit-env.nix {
exprFile = ../examples/webapps-sysvinit/processes.nix;
};
tools = import ../tools {};
nix-processmgmt = ./..;
env = "NIX_PATH=nixpkgs=${nixpkgs}";
in
makeTest {
machine =
{pkgs, ...}:
{
virtualisation.pathsInNixDB = [ pkgs.stdenv ] ++ pkgs.coreutils.all ++ [ webappUnprivileged processesEnv ];
virtualisation.writableStore = true;
users.extraUsers = {
unprivileged = {
uid = 1000;
group = "users";
shell = "/bin/sh";
description = "Unprivileged user";
home = "/home/unprivileged";
createHome = true;
};
};
# We can't download any substitutes in a test environment. To make tests
# faster, we disable substitutes so that Nix does not waste any time by
# attempting to download them.
nix.extraOptions = ''
substitute = false
'';
environment.systemPackages = [
pkgs.stdenv
pkgs.dysnomia
tools.build
tools.sysvinit
];
};
testScript = ''
def check_webapp_daemon(package):
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp start"'.format(package)
)
machine.succeed("pgrep -u unprivileged -f '/bin/webapp -D$'")
machine.succeed("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
machine.succeed(
'su - unprivileged -c "{}/etc/rc.d/init.d/webapp stop"'.format(package)
)
machine.fail("curl --fail http://localhost:5000 | grep 'Simple test webapp'")
def check_nginx_redirection():
machine.succeed(
"curl --fail -H 'Host: webapp.local' http://localhost:8080 | grep 'listening on port: 5000'"
)
def check_nginx_multi_instance_redirection():
machine.succeed(
"curl --fail -H 'Host: webapp1.local' http://localhost:8080 | grep 'listening on port: 5000'"
)
machine.succeed(
"curl --fail -H 'Host: webapp5.local' http://localhost:8081 | grep 'listening on port: 6002'"
)
def check_system_unavailable():
machine.fail("curl --fail http://localhost:8080")
machine.fail("pgrep -f '/bin/webapp'")
start_all()
# Make sure the unprivileged user can deploy
machine.succeed('su - unprivileged -c "mkdir -p var/run var/tmp"')
# Test webapp deployed by an unprivileged user
check_webapp_daemon("${webappUnprivileged}")
# Deploy the entire system as an unprivileged user
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-sysvinit/processes.nix'"
)
check_nginx_redirection()
# Upgrade to the multi-instance example and check if the redirections are done right
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-sysvinit/processes-advanced.nix'"
)
check_nginx_multi_instance_redirection()
# Undeploy the entire system as an unprivileged user
machine.succeed(
"su - unprivileged -c '${env} nixproc-sysvinit-switch --state-dir /home/unprivileged/var --force-disable-user-change ${nix-processmgmt}/examples/webapps-sysvinit/processes-empty.nix'"
)
check_system_unavailable()
# Deploy the entire system
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-sysvinit/processes.nix"
)
check_nginx_redirection()
# Upgrade to the multi-instance example and check if the redirections are done right
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-sysvinit/processes-advanced.nix"
)
check_nginx_multi_instance_redirection()
# Undeploy the entire system
machine.succeed(
"${env} nixproc-sysvinit-switch ${nix-processmgmt}/examples/webapps-sysvinit/processes-empty.nix"
)
check_system_unavailable()
'';
}

View File

@ -30,17 +30,28 @@ buildProfile()
profilePath=$(nixproc-build --process-manager $processManager $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $showTraceArg --no-out-link "$path")
}
composeProfileDir()
{
if [ "$NIX_USER_PROFILE_DIR" = "" ]
then
echo "$NIX_STATE_DIR/profiles/$profile"
else
echo "$NIX_USER_PROFILE_DIR/$profile"
fi
}
composeOldProfilePath()
{
if [ "$oldProfilePath" = "" ]
then
oldProfilePath="$NIX_STATE_DIR/profiles/$profile"
oldProfilePath=$(composeProfileDir)
fi
}
setNixProfile()
{
nix-env -p "$NIX_STATE_DIR/profiles/$profile" --set "$profilePath"
local profileDir=$(composeProfileDir)
nix-env -p "$profileDir" --set "$profilePath"
}
createNewGroups()

View File

@ -6,4 +6,10 @@ checkRunlevel()
then
runlevel=$(runlevel | cut -d ' ' -f2)
fi
# If the runlevel is unknown, fall back to 3
if [ "$runlevel" = "unknown" ]
then
runlevel=3
fi
}