Initial version of a docker target
This commit is contained in:
parent
5f976340af
commit
2451baf0bc
|
@ -32,6 +32,14 @@ Currently, the following process managers are supported:
|
|||
* `systemd`: [systemd](https://www.freedesktop.org/wiki/Software/systemd)
|
||||
* `launchd`: [launchd](https://www.launchd.info)
|
||||
* `cygrunsrv`: Cygwin's [cygrunsrv](http://web.mit.edu/cygwin/cygwin_v1.3.2/usr/doc/Cygwin/cygrunsrv.README)
|
||||
|
||||
It can also work with the following solutions that are technically not
|
||||
categorized as process managers (but can still be used as such):
|
||||
|
||||
* `docker`: [Docker](https://docker.com) is technically more than just a process
|
||||
manager, but by sharing the host's network, Nix store, and bind mounting
|
||||
relevant state directories, it can also serve as a process manager with
|
||||
similar functionality as the others described above.
|
||||
* `disnix`: Technically [Disnix](https://github.com/svanderburg/disnix) is not
|
||||
a process manager but it is flexible enough to start daemons and arrange
|
||||
activation ordering. This target backend is not designed for managing
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
let
|
||||
createManagedProcess = import ../../nixproc/create-managed-process/agnostic/create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
inherit pkgs runtimeDir stateDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
let
|
||||
createManagedProcess = import ../../nixproc/create-managed-process/agnostic/create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
inherit pkgs runtimeDir stateDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
|
||||
webappExpr = if webappMode == "foreground" then ./webapp-fg.nix
|
||||
|
|
|
@ -20,7 +20,7 @@ let
|
|||
};
|
||||
in
|
||||
rec {
|
||||
webapp = rec {
|
||||
/* webapp = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp.local";
|
||||
|
||||
|
@ -36,5 +36,5 @@ rec {
|
|||
webapps = [ webapp ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
};*/
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{pkgs, runtimeDir, tmpDir, forceDisableUserChange ? false, processManager ? null}:
|
||||
{pkgs, runtimeDir, tmpDir, stateDir, forceDisableUserChange ? false, processManager ? null}:
|
||||
|
||||
let
|
||||
basePackages = [
|
||||
|
@ -92,6 +92,15 @@ let
|
|||
inherit (pkgs) stdenv writeTextFile daemon;
|
||||
inherit createProcessScript runtimeDir tmpDir forceDisableUserChange basePackages;
|
||||
};
|
||||
|
||||
createDockerContainer = import ../docker/create-docker-container.nix {
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
|
||||
generateDockerContainer = import ../agnostic/generate-docker-container.nix {
|
||||
inherit (pkgs) stdenv writeTextFile dockerTools findutils glibc dysnomia;
|
||||
inherit createDockerContainer basePackages runtimeDir stateDir forceDisableUserChange createCredentials;
|
||||
};
|
||||
in
|
||||
import ./create-managed-process.nix {
|
||||
inherit processManager;
|
||||
|
@ -105,5 +114,6 @@ import ./create-managed-process.nix {
|
|||
else if processManager == "launchd" then generateLaunchdDaemon
|
||||
else if processManager == "cygrunsrv" then generateCygrunsrvParams
|
||||
else if processManager == "disnix" then generateProcessScript
|
||||
else if processManager == "docker" then generateDockerContainer
|
||||
else throw "Unknown process manager: ${processManager}";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
{ createDockerContainer, dockerTools, stdenv, writeTextFile, findutils, glibc, dysnomia, basePackages, runtimeDir, stateDir, forceDisableUserChange, createCredentials }:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
, postInstall
|
||||
}:
|
||||
|
||||
# umask unsupported
|
||||
# nice unsupported
|
||||
|
||||
let
|
||||
generateForegroundWrapper = import ./generate-foreground-wrapper.nix {
|
||||
inherit stdenv writeTextFile;
|
||||
};
|
||||
|
||||
cmd = if foregroundProcess != null
|
||||
then
|
||||
if initialize == null
|
||||
then [ foregroundProcess ] ++ foregroundProcessArgs
|
||||
else
|
||||
let
|
||||
wrapper = generateForegroundWrapper ({
|
||||
wrapDaemon = false;
|
||||
executable = foregroundProcess;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
});
|
||||
in
|
||||
[ wrapper ] ++ foregroundProcessArgs
|
||||
else
|
||||
let
|
||||
wrapper = generateForegroundWrapper ({
|
||||
wrapDaemon = true;
|
||||
executable = daemon;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
});
|
||||
in
|
||||
[ wrapper ] ++ daemonArgs;
|
||||
|
||||
# Remove the Nix store references so that these Nix store paths won't end up in the image.
|
||||
# Instead, we mount the host system's Nix store so that the software is still accessible inside the container.
|
||||
cmdWithoutContext = map (arg: builtins.unsafeDiscardStringContext arg) cmd;
|
||||
|
||||
_path = basePackages ++ path;
|
||||
|
||||
_environment = {
|
||||
PATH = builtins.concatStringsSep ":" (map(package: "${package}/bin" ) _path);
|
||||
} // environment;
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
|
||||
_user = if forceDisableUserChange then null else user;
|
||||
|
||||
dockerImage = dockerTools.buildImage (stdenv.lib.recursiveUpdate {
|
||||
inherit name;
|
||||
tag = "latest";
|
||||
runAsRoot = ''
|
||||
${dockerTools.shadowSetup}
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
export PATH=$PATH:${findutils}/bin:${glibc.bin}/bin
|
||||
${dysnomia}/bin/dysnomia-addgroups ${credentialsSpec}
|
||||
${dysnomia}/bin/dysnomia-addusers ${credentialsSpec}
|
||||
''}
|
||||
|
||||
${stdenv.lib.optionalString forceDisableUserChange ''
|
||||
groupadd -r nogroup
|
||||
useradd -r nobody -g nogroup -d /dev/null
|
||||
''}
|
||||
'';
|
||||
config = {
|
||||
Cmd = cmdWithoutContext;
|
||||
} // stdenv.lib.optionalAttrs (_environment != {}) {
|
||||
Env = map (varName: "${varName}=${toString (builtins.getAttr varName _environment)}") (builtins.attrNames _environment);
|
||||
} // stdenv.lib.optionalAttrs (directory != null) {
|
||||
WorkingDir = directory;
|
||||
} // stdenv.lib.optionalAttrs (_user != null) {
|
||||
User = _user;
|
||||
};
|
||||
} overrides.image or {});
|
||||
in
|
||||
createDockerContainer (stdenv.lib.recursiveUpdate {
|
||||
inherit name dockerImage postInstall cmd;
|
||||
dockerImageTag = "${name}:latest";
|
||||
useHostNixStore = true;
|
||||
useHostNetwork = true;
|
||||
mapStateDirVolume = stateDir;
|
||||
} overrides.container or {})
|
|
@ -0,0 +1,30 @@
|
|||
{ 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
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "docker";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "docker";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
{stdenv}:
|
||||
|
||||
{ name
|
||||
, dockerImage
|
||||
, dockerImageTag
|
||||
, dockerCreateParameters ? []
|
||||
, useHostNixStore ? false
|
||||
, useHostNetwork ? false
|
||||
, mapStateDirVolume ? null
|
||||
, cmd ? ""
|
||||
, storePaths ? []
|
||||
, dependencies ? []
|
||||
, postInstall ? ""
|
||||
}:
|
||||
|
||||
let
|
||||
dockerCreateParametersList =
|
||||
if builtins.isList dockerCreateParameters then dockerCreateParameters
|
||||
else if builtins.isAttrs dockerCreateParameters then map (name: { inherit name; value = builtins.getAttr name dockerCreateParameters; }) (builtins.attrNames dockerCreateParameters)
|
||||
else throw "Unknown type for the dockerCreateParameters";
|
||||
|
||||
_dockerCreateParameters = dockerCreateParametersList
|
||||
++ stdenv.lib.optional useHostNixStore { name = "volume"; value = "/nix/store:/nix/store"; }
|
||||
++ stdenv.lib.optional useHostNetwork { name = "network"; value = "host"; }
|
||||
++ stdenv.lib.optional (mapStateDirVolume != null) { name = "volume"; value = "${mapStateDirVolume}:${mapStateDirVolume}"; };
|
||||
|
||||
priority = if dependencies == [] then 1
|
||||
else builtins.head (builtins.sort (a: b: a > b) (map (dependency: dependency.priority) dependencies)) + 1;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name priority;
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
|
||||
cat > $out/${name}-docker-settings <<EOF
|
||||
dockerImage=${dockerImage}
|
||||
dockerImageTag=${name}:latest
|
||||
EOF
|
||||
|
||||
cat > $out/${name}-docker-createparams <<EOF
|
||||
${stdenv.lib.concatMapStringsSep "\n" (nameValuePair:
|
||||
"${if builtins.stringLength nameValuePair.name > 1 then "--" else "-"}${nameValuePair.name}\n"
|
||||
+ "${toString nameValuePair.value}"
|
||||
) _dockerCreateParameters}
|
||||
EOF
|
||||
|
||||
echo "${toString priority}" > $out/${name}-docker-priority
|
||||
|
||||
${stdenv.lib.optionalString useHostNixStore ''
|
||||
# Add configuration files with Nix store paths used from the host system so that they will not be garbage collected
|
||||
${stdenv.lib.optionalString (cmd != "") ''
|
||||
cat > $out/${name}-docker-cmd <<EOF
|
||||
${toString cmd}
|
||||
EOF
|
||||
''}
|
||||
|
||||
${stdenv.lib.optionalString (storePaths != []) ''
|
||||
cat > $out/${name}-storepaths <<EOF
|
||||
${stdenv.lib.concatMapStrings (storePath: "${storePath}\n") storePaths}
|
||||
EOF
|
||||
''}
|
||||
''}
|
||||
${postInstall}
|
||||
'';
|
||||
}
|
|
@ -23,6 +23,10 @@ rec {
|
|||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
docker = import ./docker {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
launchd = import ./launchd {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-docker-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
-e "s|@xargs@|$(type -p xargs)|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
${./nixproc-docker-switch.in} > $out/bin/nixproc-docker-switch
|
||||
chmod +x $out/bin/nixproc-docker-switch
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command repopulates a folder with systemd configuration files and updates
|
||||
the configuration so that obsolete services will be stoppped and new services
|
||||
will be started.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
SYSTEMD_TARGET_DIR Directory in which the unit configuration files are
|
||||
managed (defaults to: /etc/systemd/system)
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
forceDisableUserChange=1
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Build the environment with docker container config files
|
||||
buildProfile docker
|
||||
|
||||
deployContainer()
|
||||
{
|
||||
local configDir="$1"
|
||||
local containerName="$2"
|
||||
|
||||
(
|
||||
source $configDir/$containerName-docker-settings
|
||||
dockerContainerName="nixproc-$containerName"
|
||||
|
||||
# Load the Docker image if it does not exists
|
||||
if [ "$(docker images -f "reference=$dockerImageTag" | wc -l)" = "1" ]
|
||||
then
|
||||
docker load -i $dockerImage
|
||||
fi
|
||||
|
||||
# Create the container if it does not exists yet
|
||||
if [ "$(docker ps -a -f "name=$dockerContainerName" | wc -l)" = "1" ]
|
||||
then
|
||||
(
|
||||
cat $configDir/*-docker-createparams
|
||||
echo "--name"
|
||||
echo "$dockerContainerName"
|
||||
|
||||
if [ "$forceDisableUserChange" = "1" ]
|
||||
then
|
||||
echo "--user"
|
||||
id -u
|
||||
fi
|
||||
|
||||
echo "$dockerImageTag"
|
||||
) | @xargs@ -d '\n' docker create
|
||||
fi
|
||||
|
||||
if [ "$(docker ps -f "name=$dockerContainerName" | wc -l)" = "1" ]
|
||||
then
|
||||
docker start $dockerContainerName
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
undeployContainer()
|
||||
{
|
||||
local configDir="$1"
|
||||
local containerName="$2"
|
||||
|
||||
(
|
||||
source $configDir/$containerName-docker-settings
|
||||
dockerContainerName="nixproc-$containerName"
|
||||
dockerStopTimeout=${dockerStopTimeout:-1}
|
||||
|
||||
if [ "$(docker ps -f "name=$dockerContainerName" | wc -l)" = "2" ]
|
||||
then
|
||||
docker stop -t $dockerStopTimeout $dockerContainerName
|
||||
fi
|
||||
|
||||
if [ "$(docker ps -a -f "name=$dockerContainerName" | wc -l)" = "2" ]
|
||||
then
|
||||
docker rm $dockerContainerName
|
||||
fi
|
||||
|
||||
if [ "$(docker images -f "reference=$dockerImageTag" | wc -l)" = "2" ]
|
||||
then
|
||||
docker rmi $dockerImageTag
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
# Determine paths of old containers
|
||||
|
||||
oldunits=()
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
for i in $oldProfilePath/*-docker-settings
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
oldunits+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new containers
|
||||
|
||||
newunits=()
|
||||
|
||||
for i in $profilePath/*-docker-settings
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
newunits+=($currentPath)
|
||||
done
|
||||
|
||||
if [ "$oldProfilePath" != "" ]
|
||||
then
|
||||
# Undeploy obsolete containers
|
||||
|
||||
for i in $oldProfilePath/*-docker-settings
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${newunits[@]}"
|
||||
then
|
||||
undeployContainer "$(dirname "$i")" "$(basename "$i" -docker-settings)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Deploy new containers
|
||||
|
||||
for i in $profilePath/*-docker-settings
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${oldunits[@]}"
|
||||
then
|
||||
deployContainer "$(dirname "$i")" "$(basename "$i" -docker-settings)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
Loading…
Reference in New Issue