mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 16:33:15 +00:00
add docs to docker build functions
bring back ls_tar replace goPackages with go don't hardcode /nix/store in vmTools more docs
This commit is contained in:
parent
58dc2f9d49
commit
4abe579250
@ -1,5 +1,23 @@
|
|||||||
{ stdenv, lib, callPackage, runCommand, writeReferencesToFile, writeText, vmTools, writeScript
|
{
|
||||||
, docker, shadow, utillinux, coreutils, jshon, e2fsprogs, go, pigz, findutils }:
|
callPackage,
|
||||||
|
coreutils,
|
||||||
|
docker,
|
||||||
|
e2fsprogs,
|
||||||
|
findutils,
|
||||||
|
go,
|
||||||
|
jshon,
|
||||||
|
lib,
|
||||||
|
pigz,
|
||||||
|
runCommand,
|
||||||
|
shadow,
|
||||||
|
stdenv,
|
||||||
|
storeDir ? builtins.storeDir,
|
||||||
|
utillinux,
|
||||||
|
vmTools,
|
||||||
|
writeReferencesToFile,
|
||||||
|
writeScript,
|
||||||
|
writeText,
|
||||||
|
}:
|
||||||
|
|
||||||
# WARNING: this API is unstable and may be subject to backwards-incompatible changes in the future.
|
# WARNING: this API is unstable and may be subject to backwards-incompatible changes in the future.
|
||||||
|
|
||||||
@ -25,65 +43,76 @@ rec {
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
# buildEnv creates symlinks to dirs, which is hard to edit inside the overlay VM
|
# buildEnv creates symlinks to dirs, which is hard to edit inside the overlay VM
|
||||||
mergeDrvs = { drvs, onlyDeps ? false }:
|
mergeDrvs = {
|
||||||
|
derivations,
|
||||||
|
onlyDeps ? false
|
||||||
|
}:
|
||||||
runCommand "merge-drvs" {
|
runCommand "merge-drvs" {
|
||||||
inherit drvs onlyDeps;
|
inherit derivations onlyDeps;
|
||||||
} ''
|
} ''
|
||||||
if [ -n "$onlyDeps" ]; then
|
if [[ -n "$onlyDeps" ]]; then
|
||||||
echo $drvs > $out
|
echo $derivations > $out
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir $out
|
mkdir $out
|
||||||
for drv in $drvs; do
|
for derivation in $derivations; do
|
||||||
echo Merging $drv
|
echo "Merging $derivation..."
|
||||||
if [ -d "$drv" ]; then
|
if [[ -d "$derivation" ]]; then
|
||||||
cp -drf --preserve=mode -f $drv/* $out/
|
# If it's a directory, copy all of its contents into $out.
|
||||||
|
cp -drf --preserve=mode -f $derivation/* $out/
|
||||||
else
|
else
|
||||||
|
# Otherwise treat the derivation as a tarball and extract it
|
||||||
|
# into $out.
|
||||||
tar -C $out -xpf $drv || true
|
tar -C $out -xpf $drv || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
|
|
||||||
shellScript = text:
|
# Helper for setting up the base files for managing users and
|
||||||
writeScript "script.sh" ''
|
# groups, only if such files don't exist already. It is suitable for
|
||||||
#!${stdenv.shell}
|
# being used in a runAsRoot script.
|
||||||
set -e
|
|
||||||
export PATH=${coreutils}/bin:/bin
|
|
||||||
|
|
||||||
${text}
|
|
||||||
'';
|
|
||||||
|
|
||||||
shadowSetup = ''
|
shadowSetup = ''
|
||||||
export PATH=${shadow}/bin:$PATH
|
export PATH=${shadow}/bin:$PATH
|
||||||
mkdir -p /etc/pam.d
|
mkdir -p /etc/pam.d
|
||||||
if [ ! -f /etc/passwd ]; then
|
if [[ ! -f /etc/passwd ]]; then
|
||||||
echo "root:x:0:0::/root:/bin/sh" > /etc/passwd
|
echo "root:x:0:0::/root:/bin/sh" > /etc/passwd
|
||||||
echo "root:!x:::::::" > /etc/shadow
|
echo "root:!x:::::::" > /etc/shadow
|
||||||
fi
|
fi
|
||||||
if [ ! -f /etc/group ]; then
|
if [[ ! -f /etc/group ]]; then
|
||||||
echo "root:x:0:" > /etc/group
|
echo "root:x:0:" > /etc/group
|
||||||
echo "root:x::" > /etc/gshadow
|
echo "root:x::" > /etc/gshadow
|
||||||
fi
|
fi
|
||||||
if [ ! -f /etc/pam.d/other ]; then
|
if [[ ! -f /etc/pam.d/other ]]; then
|
||||||
cat > /etc/pam.d/other <<EOF
|
cat > /etc/pam.d/other <<EOF
|
||||||
account sufficient pam_unix.so
|
account sufficient pam_unix.so
|
||||||
auth sufficient pam_rootok.so
|
auth sufficient pam_rootok.so
|
||||||
password requisite pam_unix.so nullok sha512
|
password requisite pam_unix.so nullok sha512
|
||||||
session required pam_unix.so
|
session required pam_unix.so
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
if [ ! -f /etc/login.defs ]; then
|
if [[ ! -f /etc/login.defs ]]; then
|
||||||
touch /etc/login.defs
|
touch /etc/login.defs
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
runWithOverlay = { name , fromImage ? null, fromImageName ? null, fromImageTag ? null
|
# Run commands in a virtual machine.
|
||||||
, diskSize ? 1024, preMount ? "", postMount ? "", postUmount ? "" }:
|
runWithOverlay = {
|
||||||
|
name,
|
||||||
|
fromImage ? null,
|
||||||
|
fromImageName ? null,
|
||||||
|
fromImageTag ? null,
|
||||||
|
diskSize ? 1024,
|
||||||
|
preMount ? "",
|
||||||
|
postMount ? "",
|
||||||
|
postUmount ? ""
|
||||||
|
}:
|
||||||
vmTools.runInLinuxVM (
|
vmTools.runInLinuxVM (
|
||||||
runCommand name {
|
runCommand name {
|
||||||
preVM = vmTools.createEmptyImage { size = diskSize; fullName = "docker-run-disk"; };
|
preVM = vmTools.createEmptyImage {
|
||||||
|
size = diskSize;
|
||||||
|
fullName = "docker-run-disk";
|
||||||
|
};
|
||||||
inherit fromImage fromImageName fromImageTag;
|
inherit fromImage fromImageName fromImageTag;
|
||||||
|
|
||||||
buildInputs = [ utillinux e2fsprogs jshon ];
|
buildInputs = [ utillinux e2fsprogs jshon ];
|
||||||
@ -95,38 +124,55 @@ EOF
|
|||||||
mount /dev/${vmTools.hd} disk
|
mount /dev/${vmTools.hd} disk
|
||||||
cd disk
|
cd disk
|
||||||
|
|
||||||
if [ -n "$fromImage" ]; then
|
if [[ -n "$fromImage" ]]; then
|
||||||
echo Unpacking base image
|
echo "Unpacking base image..."
|
||||||
mkdir image
|
mkdir image
|
||||||
tar -C image -xpf "$fromImage"
|
tar -C image -xpf "$fromImage"
|
||||||
|
|
||||||
if [ -z "$fromImageName" ]; then
|
# If the image name isn't set, read it from the image repository json.
|
||||||
fromImageName=$(jshon -k < image/repositories|head -n1)
|
if [[ -z "$fromImageName" ]]; then
|
||||||
|
fromImageName=$(jshon -k < image/repositories | head -n 1)
|
||||||
|
echo "From-image name wasn't set. Read $fromImageName."
|
||||||
fi
|
fi
|
||||||
if [ -z "$fromImageTag" ]; then
|
|
||||||
fromImageTag=$(jshon -e $fromImageName -k < image/repositories|head -n1)
|
# If the tag isn't set, use the name as an index into the json
|
||||||
|
# and read the first key found.
|
||||||
|
if [[ -z "$fromImageTag" ]]; then
|
||||||
|
fromImageTag=$(jshon -e $fromImageName -k < image/repositories \
|
||||||
|
| head -n1)
|
||||||
|
echo "From-image tag wasn't set. Read $fromImageTag."
|
||||||
fi
|
fi
|
||||||
parentID=$(jshon -e $fromImageName -e $fromImageTag -u < image/repositories)
|
|
||||||
|
# Use the name and tag to get the parent ID field.
|
||||||
|
parentID=$(jshon -e $fromImageName -e $fromImageTag -u \
|
||||||
|
< image/repositories)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Unpack all of the parent layers into the image.
|
||||||
lowerdir=""
|
lowerdir=""
|
||||||
while [ -n "$parentID" ]; do
|
while [[ -n "$parentID" ]]; do
|
||||||
echo Unpacking layer $parentID
|
echo "Unpacking layer $parentID"
|
||||||
mkdir -p image/$parentID/layer
|
mkdir -p image/$parentID/layer
|
||||||
tar -C image/$parentID/layer -xpf image/$parentID/layer.tar
|
tar -C image/$parentID/layer -xpf image/$parentID/layer.tar
|
||||||
rm image/$parentID/layer.tar
|
rm image/$parentID/layer.tar
|
||||||
|
|
||||||
find image/$parentID/layer -name ".wh.*" -exec bash -c 'name="$(basename {}|sed "s/^.wh.//")"; mknod "$(dirname {})/$name" c 0 0; rm {}' \;
|
find image/$parentID/layer -name ".wh.*" -exec bash -c 'name="$(basename {}|sed "s/^.wh.//")"; mknod "$(dirname {})/$name" c 0 0; rm {}' \;
|
||||||
|
|
||||||
|
# Get the next lower directory and continue the loop.
|
||||||
lowerdir=$lowerdir''${lowerdir:+:}image/$parentID/layer
|
lowerdir=$lowerdir''${lowerdir:+:}image/$parentID/layer
|
||||||
parentID=$(cat image/$parentID/json|(jshon -e parent -u 2>/dev/null || true))
|
parentID=$(cat image/$parentID/json \
|
||||||
|
| (jshon -e parent -u 2>/dev/null || true))
|
||||||
done
|
done
|
||||||
|
|
||||||
mkdir work
|
mkdir work
|
||||||
mkdir layer
|
mkdir layer
|
||||||
mkdir mnt
|
mkdir mnt
|
||||||
|
|
||||||
${preMount}
|
${lib.optionalString (preMount != "") ''
|
||||||
|
# Execute pre-mount steps
|
||||||
|
echo "Executing pre-mount steps..."
|
||||||
|
${preMount}
|
||||||
|
''}
|
||||||
|
|
||||||
if [ -n "$lowerdir" ]; then
|
if [ -n "$lowerdir" ]; then
|
||||||
mount -t overlay overlay -olowerdir=$lowerdir,workdir=work,upperdir=layer mnt
|
mount -t overlay overlay -olowerdir=$lowerdir,workdir=work,upperdir=layer mnt
|
||||||
@ -134,13 +180,19 @@ EOF
|
|||||||
mount --bind layer mnt
|
mount --bind layer mnt
|
||||||
fi
|
fi
|
||||||
|
|
||||||
${postMount}
|
${lib.optionalString (postMount != "") ''
|
||||||
|
# Execute post-mount steps
|
||||||
|
echo "Executing post-mount steps..."
|
||||||
|
${postMount}
|
||||||
|
''}
|
||||||
|
|
||||||
umount mnt
|
umount mnt
|
||||||
|
|
||||||
pushd layer
|
(
|
||||||
find . -type c -exec bash -c 'name="$(basename {})"; touch "$(dirname {})/.wh.$name"; rm "{}"' \;
|
cd layer
|
||||||
popd
|
cmd='name="$(basename {})"; touch "$(dirname {})/.wh.$name"; rm "{}"'
|
||||||
|
find . -type c -exec bash -c "$cmd" \;
|
||||||
|
)
|
||||||
|
|
||||||
${postUmount}
|
${postUmount}
|
||||||
'');
|
'');
|
||||||
@ -150,76 +202,150 @@ EOF
|
|||||||
inherit name fromImage fromImageName fromImageTag diskSize;
|
inherit name fromImage fromImageName fromImageTag diskSize;
|
||||||
|
|
||||||
postMount = ''
|
postMount = ''
|
||||||
echo Packing raw image
|
echo "Packing raw image..."
|
||||||
tar -C mnt --mtime=0 -cf $out .
|
tar -C mnt --mtime=0 -cf $out .
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
mkPureLayer = { baseJson, contents ? null, extraCommands ? "" }:
|
|
||||||
runCommand "docker-layer" {
|
# Create an executable shell script which has the coreutils in its
|
||||||
|
# PATH. Since root scripts are executed in a blank environment, even
|
||||||
|
# things like `ls` or `echo` will be missing.
|
||||||
|
shellScript = name: text:
|
||||||
|
writeScript name ''
|
||||||
|
#!${stdenv.shell}
|
||||||
|
set -e
|
||||||
|
export PATH=${coreutils}/bin:/bin
|
||||||
|
${text}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Create a "layer" (set of files).
|
||||||
|
mkPureLayer = {
|
||||||
|
# Name of the layer
|
||||||
|
name,
|
||||||
|
# JSON containing configuration and metadata for this layer.
|
||||||
|
baseJson,
|
||||||
|
# Files to add to the layer.
|
||||||
|
contents ? null,
|
||||||
|
# Additional commands to run on the layer before it is tar'd up.
|
||||||
|
extraCommands ? ""
|
||||||
|
}:
|
||||||
|
runCommand "docker-layer-${name}" {
|
||||||
inherit baseJson contents extraCommands;
|
inherit baseJson contents extraCommands;
|
||||||
|
|
||||||
buildInputs = [ jshon ];
|
buildInputs = [ jshon ];
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
mkdir layer
|
mkdir layer
|
||||||
if [ -n "$contents" ]; then
|
if [[ -n "$contents" ]]; then
|
||||||
echo Adding contents
|
echo "Adding contents..."
|
||||||
for c in $contents; do
|
for item in $contents; do
|
||||||
cp -drf $c/* layer/
|
echo "Adding $item"
|
||||||
chmod -R ug+w layer/
|
cp -drf $item/* layer/
|
||||||
done
|
done
|
||||||
|
chmod -R ug+w layer/
|
||||||
|
else
|
||||||
|
echo "No contents to add to layer."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pushd layer
|
if [[ -n $extraCommands ]]; then
|
||||||
${extraCommands}
|
(cd layer; eval "$extraCommands")
|
||||||
popd
|
fi
|
||||||
|
|
||||||
echo Packing layer
|
# Tar up the layer and throw it into 'layer.tar'.
|
||||||
|
echo "Packing layer..."
|
||||||
mkdir $out
|
mkdir $out
|
||||||
tar -C layer --mtime=0 -cf $out/layer.tar .
|
tar -C layer --mtime=0 -cf $out/layer.tar .
|
||||||
ts=$(${tarsum} < $out/layer.tar)
|
|
||||||
cat ${baseJson} | jshon -s "$ts" -i checksum > $out/json
|
# Compute a checksum of the tarball.
|
||||||
|
echo "Computing layer checksum..."
|
||||||
|
tarsum=$(${tarsum} < $out/layer.tar)
|
||||||
|
|
||||||
|
# Add a 'checksum' field to the JSON, with the value set to the
|
||||||
|
# checksum of the tarball.
|
||||||
|
cat ${baseJson} | jshon -s "$tarsum" -i checksum > $out/json
|
||||||
|
|
||||||
|
# Indicate to docker that we're using schema version 1.0.
|
||||||
echo -n "1.0" > $out/VERSION
|
echo -n "1.0" > $out/VERSION
|
||||||
|
|
||||||
|
echo "Finished building layer '${name}'"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
mkRootLayer = { runAsRoot, baseJson, fromImage ? null, fromImageName ? null, fromImageTag ? null
|
# Make a "root" layer; required if we need to execute commands as a
|
||||||
, diskSize ? 1024, contents ? null, extraCommands ? "" }:
|
# privileged user on the image. The commands themselves will be
|
||||||
let runAsRootScript = writeScript "run-as-root.sh" runAsRoot;
|
# performed in a virtual machine sandbox.
|
||||||
|
mkRootLayer = {
|
||||||
|
# Name of the image.
|
||||||
|
name,
|
||||||
|
# Script to run as root. Bash.
|
||||||
|
runAsRoot,
|
||||||
|
# Files to add to the layer. If null, an empty layer will be created.
|
||||||
|
contents ? null,
|
||||||
|
# JSON containing configuration and metadata for this layer.
|
||||||
|
baseJson,
|
||||||
|
# Existing image onto which to append the new layer.
|
||||||
|
fromImage ? null,
|
||||||
|
# Name of the image we're appending onto.
|
||||||
|
fromImageName ? null,
|
||||||
|
# Tag of the image we're appending onto.
|
||||||
|
fromImageTag ? null,
|
||||||
|
# How much disk to allocate for the temporary virtual machine.
|
||||||
|
diskSize ? 1024,
|
||||||
|
# Commands (bash) to run on the layer; these do not require sudo.
|
||||||
|
extraCommands ? ""
|
||||||
|
}:
|
||||||
|
# Generate an executable script from the `runAsRoot` text.
|
||||||
|
let runAsRootScript = shellScript "run-as-root.sh" runAsRoot;
|
||||||
in runWithOverlay {
|
in runWithOverlay {
|
||||||
name = "docker-layer";
|
name = "docker-layer-${name}";
|
||||||
|
|
||||||
inherit fromImage fromImageName fromImageTag diskSize;
|
inherit fromImage fromImageName fromImageTag diskSize;
|
||||||
|
|
||||||
preMount = lib.optionalString (contents != null) ''
|
preMount = lib.optionalString (contents != null && contents != []) ''
|
||||||
echo Adding contents
|
echo "Adding contents..."
|
||||||
for c in ${builtins.toString contents}; do
|
for item in ${toString contents}; do
|
||||||
cp -drf $c/* layer/
|
echo "Adding $item..."
|
||||||
chmod -R ug+w layer/
|
cp -drf $item/* layer/
|
||||||
done
|
done
|
||||||
|
chmod -R ug+w layer/
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postMount = ''
|
postMount = ''
|
||||||
mkdir -p mnt/{dev,proc,sys,nix/store}
|
mkdir -p mnt/{dev,proc,sys} mnt${storeDir}
|
||||||
|
|
||||||
|
# Mount /dev, /sys and the nix store as shared folders.
|
||||||
mount --rbind /dev mnt/dev
|
mount --rbind /dev mnt/dev
|
||||||
mount --rbind /sys mnt/sys
|
mount --rbind /sys mnt/sys
|
||||||
mount --rbind /nix/store mnt/nix/store
|
mount --rbind ${storeDir} mnt${storeDir}
|
||||||
|
|
||||||
|
# Execute the run as root script. See 'man unshare' for
|
||||||
|
# details on what's going on here; basically this command
|
||||||
|
# means that the runAsRootScript will be executed in a nearly
|
||||||
|
# completely isolated environment.
|
||||||
unshare -imnpuf --mount-proc chroot mnt ${runAsRootScript}
|
unshare -imnpuf --mount-proc chroot mnt ${runAsRootScript}
|
||||||
umount -R mnt/dev mnt/sys mnt/nix/store
|
|
||||||
rmdir --ignore-fail-on-non-empty mnt/dev mnt/proc mnt/sys mnt/nix/store mnt/nix
|
# Unmount directories and remove them.
|
||||||
|
umount -R mnt/dev mnt/sys mnt${storeDir}
|
||||||
|
rmdir --ignore-fail-on-non-empty \
|
||||||
|
mnt/dev mnt/proc mnt/sys mnt${storeDir} \
|
||||||
|
mnt$(dirname ${storeDir})
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postUmount = ''
|
postUmount = ''
|
||||||
pushd layer
|
(cd layer; eval "${extraCommands}")
|
||||||
${extraCommands}
|
|
||||||
popd
|
|
||||||
|
|
||||||
echo Packing layer
|
echo "Packing layer..."
|
||||||
mkdir $out
|
mkdir $out
|
||||||
tar -C layer --mtime=0 -cf $out/layer.tar .
|
tar -C layer --mtime=0 -cf $out/layer.tar .
|
||||||
|
|
||||||
|
# Compute the tar checksum and add it to the output json.
|
||||||
|
echo "Computing checksum..."
|
||||||
ts=$(${tarsum} < $out/layer.tar)
|
ts=$(${tarsum} < $out/layer.tar)
|
||||||
cat ${baseJson} | jshon -s "$ts" -i checksum > $out/json
|
cat ${baseJson} | jshon -s "$ts" -i checksum > $out/json
|
||||||
|
# Indicate to docker that we're using schema version 1.0.
|
||||||
echo -n "1.0" > $out/VERSION
|
echo -n "1.0" > $out/VERSION
|
||||||
|
|
||||||
|
echo "Finished building layer '${name}'"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -229,66 +355,86 @@ EOF
|
|||||||
# 4. compute the layer id
|
# 4. compute the layer id
|
||||||
# 5. put the layer in the image
|
# 5. put the layer in the image
|
||||||
# 6. repack the image
|
# 6. repack the image
|
||||||
buildImage = args@{ name, tag ? "latest"
|
buildImage = args@{
|
||||||
, fromImage ? null, fromImageName ? null, fromImageTag ? null
|
# Image name.
|
||||||
, contents ? null, config ? null, runAsRoot ? null
|
name,
|
||||||
, diskSize ? 1024, extraCommands ? "" }:
|
# Image tag.
|
||||||
|
tag ? "latest",
|
||||||
|
# Parent image, to append to.
|
||||||
|
fromImage ? null,
|
||||||
|
# Name of the parent image; will be read from the image otherwise.
|
||||||
|
fromImageName ? null,
|
||||||
|
# Tag of the parent image; will be read from the image otherwise.
|
||||||
|
fromImageTag ? null,
|
||||||
|
# Files to put on the image (a nix store path or list of paths).
|
||||||
|
contents ? null,
|
||||||
|
# Docker config; e.g. what command to run on the container.
|
||||||
|
config ? null,
|
||||||
|
# Optional bash script to run on the files prior to fixturizing the layer.
|
||||||
|
extraCommands ? "",
|
||||||
|
# Optional bash script to run as root on the image when provisioning.
|
||||||
|
runAsRoot ? null,
|
||||||
|
# Size of the virtual machine disk to provision when building the image.
|
||||||
|
diskSize ? 1024,
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
baseName = baseNameOf name;
|
baseName = baseNameOf name;
|
||||||
|
|
||||||
|
# Create a JSON blob of the configuration. Set the date to unix zero.
|
||||||
baseJson = writeText "${baseName}-config.json" (builtins.toJSON {
|
baseJson = writeText "${baseName}-config.json" (builtins.toJSON {
|
||||||
created = "1970-01-01T00:00:01Z";
|
created = "1970-01-01T00:00:01Z";
|
||||||
architecture = "amd64";
|
architecture = "amd64";
|
||||||
os = "linux";
|
os = "linux";
|
||||||
config = config;
|
config = config;
|
||||||
});
|
});
|
||||||
|
|
||||||
layer = (if runAsRoot == null
|
layer =
|
||||||
then mkPureLayer { inherit baseJson contents extraCommands; }
|
if runAsRoot == null
|
||||||
else mkRootLayer { inherit baseJson fromImage fromImageName fromImageTag contents runAsRoot diskSize extraCommands; });
|
then mkPureLayer { inherit name baseJson contents extraCommands; }
|
||||||
result = runCommand "${baseName}.tar.gz" {
|
else mkRootLayer { inherit name baseJson fromImage fromImageName
|
||||||
|
fromImageTag contents runAsRoot diskSize
|
||||||
|
extraCommands; };
|
||||||
|
result = runCommand "docker-image-${baseName}.tar.gz" {
|
||||||
buildInputs = [ jshon pigz coreutils findutils ];
|
buildInputs = [ jshon pigz coreutils findutils ];
|
||||||
|
|
||||||
imageName = name;
|
imageName = name;
|
||||||
imageTag = tag;
|
imageTag = tag;
|
||||||
inherit fromImage baseJson;
|
inherit fromImage baseJson;
|
||||||
|
|
||||||
layerClosure = writeReferencesToFile layer;
|
layerClosure = writeReferencesToFile layer;
|
||||||
|
passthru.buildArgs = args;
|
||||||
passthru = {
|
passthru.layer = layer;
|
||||||
buildArgs = args;
|
|
||||||
};
|
|
||||||
} ''
|
} ''
|
||||||
# Print tar contents:
|
# Print tar contents:
|
||||||
# 1: Interpreted as relative to the root directory
|
# 1: Interpreted as relative to the root directory
|
||||||
# 2: With no trailing slashes on directories
|
# 2: With no trailing slashes on directories
|
||||||
# This is useful for ensuring that the output matches the values generated by the "find" command
|
# This is useful for ensuring that the output matches the
|
||||||
|
# values generated by the "find" command
|
||||||
ls_tar() {
|
ls_tar() {
|
||||||
for f in $(tar -tf $1 | xargs realpath -ms --relative-to=.); do
|
for f in $(tar -tf $1 | xargs realpath -ms --relative-to=.); do
|
||||||
if [ "$f" != "." ]; then
|
if [[ "$f" != "." ]]; then
|
||||||
echo "/$f"
|
echo "/$f"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir image
|
mkdir image
|
||||||
touch baseFiles
|
touch baseFiles
|
||||||
if [ -n "$fromImage" ]; then
|
if [[ -n "$fromImage" ]]; then
|
||||||
echo Unpacking base image
|
echo "Unpacking base image..."
|
||||||
tar -C image -xpf "$fromImage"
|
tar -C image -xpf "$fromImage"
|
||||||
|
|
||||||
if [ -z "$fromImageName" ]; then
|
if [[ -z "$fromImageName" ]]; then
|
||||||
fromImageName=$(jshon -k < image/repositories|head -n1)
|
fromImageName=$(jshon -k < image/repositories|head -n1)
|
||||||
fi
|
fi
|
||||||
if [ -z "$fromImageTag" ]; then
|
if [[ -z "$fromImageTag" ]]; then
|
||||||
fromImageTag=$(jshon -e $fromImageName -k < image/repositories|head -n1)
|
fromImageTag=$(jshon -e $fromImageName -k \
|
||||||
|
< image/repositories|head -n1)
|
||||||
fi
|
fi
|
||||||
parentID=$(jshon -e $fromImageName -e $fromImageTag -u < image/repositories)
|
parentID=$(jshon -e $fromImageName -e $fromImageTag -u \
|
||||||
|
< image/repositories)
|
||||||
|
|
||||||
for l in image/*/layer.tar; do
|
for l in image/*/layer.tar; do
|
||||||
ls_tar $l >> baseFiles
|
ls_tar image/*/layer.tar >> baseFiles
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -298,47 +444,55 @@ EOF
|
|||||||
cp ${layer}/* temp/
|
cp ${layer}/* temp/
|
||||||
chmod ug+w temp/*
|
chmod ug+w temp/*
|
||||||
|
|
||||||
|
echo "$(dirname ${storeDir})" >> layerFiles
|
||||||
|
echo '${storeDir}' >> layerFiles
|
||||||
for dep in $(cat $layerClosure); do
|
for dep in $(cat $layerClosure); do
|
||||||
find $dep -path "${layer}" -prune -o -print >> layerFiles
|
find $dep >> layerFiles
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -s layerFiles ]; then
|
echo "Adding layer..."
|
||||||
# FIXME: might not be /nix/store
|
# Record the contents of the tarball with ls_tar.
|
||||||
echo '/nix' >> layerFiles
|
|
||||||
echo '/nix/store' >> layerFiles
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo Adding layer
|
|
||||||
ls_tar temp/layer.tar >> baseFiles
|
ls_tar temp/layer.tar >> baseFiles
|
||||||
comm <(sort -u baseFiles) <(sort -u layerFiles) -1 -3 > newFiles
|
|
||||||
tar -rpf temp/layer.tar --mtime=0 --no-recursion --files-from newFiles 2>/dev/null || true
|
|
||||||
|
|
||||||
echo Adding meta
|
# Get the files in the new layer which were *not* present in
|
||||||
|
# the old layer, and record them as newFiles.
|
||||||
|
comm <(sort -n baseFiles|uniq) \
|
||||||
|
<(sort -n layerFiles|uniq|grep -v ${layer}) -1 -3 > newFiles
|
||||||
|
# Append the new files to the layer.
|
||||||
|
tar -rpf temp/layer.tar --mtime=0 --no-recursion --files-from newFiles
|
||||||
|
|
||||||
if [ -n "$parentID" ]; then
|
echo "Adding meta..."
|
||||||
|
|
||||||
|
# If we have a parentID, add it to the json metadata.
|
||||||
|
if [[ -n "$parentID" ]]; then
|
||||||
cat temp/json | jshon -s "$parentID" -i parent > tmpjson
|
cat temp/json | jshon -s "$parentID" -i parent > tmpjson
|
||||||
mv tmpjson temp/json
|
mv tmpjson temp/json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Take the sha256 sum of the generated json and use it as the layer ID.
|
||||||
|
# Compute the size and add it to the json under the 'Size' field.
|
||||||
layerID=$(sha256sum temp/json|cut -d ' ' -f 1)
|
layerID=$(sha256sum temp/json|cut -d ' ' -f 1)
|
||||||
size=$(stat --printf="%s" temp/layer.tar)
|
size=$(stat --printf="%s" temp/layer.tar)
|
||||||
cat temp/json | jshon -s "$layerID" -i id -n $size -i Size > tmpjson
|
cat temp/json | jshon -s "$layerID" -i id -n $size -i Size > tmpjson
|
||||||
mv tmpjson temp/json
|
mv tmpjson temp/json
|
||||||
|
|
||||||
|
# Use the temp folder we've been working on to create a new image.
|
||||||
mv temp image/$layerID
|
mv temp image/$layerID
|
||||||
|
|
||||||
|
# Store the json under the name image/repositories.
|
||||||
jshon -n object \
|
jshon -n object \
|
||||||
-n object -s "$layerID" -i "$imageTag" \
|
-n object -s "$layerID" -i "$imageTag" \
|
||||||
-i "$imageName" > image/repositories
|
-i "$imageName" > image/repositories
|
||||||
|
|
||||||
|
# Make the image read-only.
|
||||||
chmod -R a-w image
|
chmod -R a-w image
|
||||||
|
|
||||||
echo Cooking the image
|
echo "Cooking the image..."
|
||||||
tar -C image --mtime=0 -c . | pigz -nT > $out
|
tar -C image --mtime=0 -c . | pigz -nT > $out
|
||||||
|
|
||||||
|
echo "Finished."
|
||||||
'';
|
'';
|
||||||
|
|
||||||
in
|
in
|
||||||
|
result;
|
||||||
result;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{ pkgs
|
{ pkgs
|
||||||
, kernel ? pkgs.linux
|
, kernel ? pkgs.linux
|
||||||
, img ? "bzImage"
|
, img ? "bzImage"
|
||||||
|
, storeDir ? builtins.storeDir
|
||||||
, rootModules ?
|
, rootModules ?
|
||||||
[ "virtio_pci" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "rtc_cmos" ]
|
[ "virtio_pci" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "rtc_cmos" ]
|
||||||
}:
|
}:
|
||||||
@ -128,8 +129,8 @@ rec {
|
|||||||
mount -t devpts none /fs/dev/pts
|
mount -t devpts none /fs/dev/pts
|
||||||
|
|
||||||
echo "mounting Nix store..."
|
echo "mounting Nix store..."
|
||||||
mkdir -p /fs/nix/store
|
mkdir -p /fs${storeDir}
|
||||||
mount -t 9p store /fs/nix/store -o trans=virtio,version=9p2000.L,cache=loose
|
mount -t 9p store /fs${storeDir} -o trans=virtio,version=9p2000.L,cache=loose
|
||||||
|
|
||||||
mkdir -p /fs/tmp /fs/run /fs/var
|
mkdir -p /fs/tmp /fs/run /fs/var
|
||||||
mount -t tmpfs -o "mode=1777" none /fs/tmp
|
mount -t tmpfs -o "mode=1777" none /fs/tmp
|
||||||
@ -172,7 +173,7 @@ rec {
|
|||||||
# apparent KVM > 1.5.2 bug.
|
# apparent KVM > 1.5.2 bug.
|
||||||
${pkgs.utillinux}/bin/hwclock -s
|
${pkgs.utillinux}/bin/hwclock -s
|
||||||
|
|
||||||
export NIX_STORE=/nix/store
|
export NIX_STORE=${storeDir}
|
||||||
export NIX_BUILD_TOP=/tmp
|
export NIX_BUILD_TOP=/tmp
|
||||||
export TMPDIR=/tmp
|
export TMPDIR=/tmp
|
||||||
export PATH=/empty
|
export PATH=/empty
|
||||||
@ -220,7 +221,7 @@ rec {
|
|||||||
${lib.optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
|
${lib.optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
|
||||||
-nographic -no-reboot \
|
-nographic -no-reboot \
|
||||||
-device virtio-rng-pci \
|
-device virtio-rng-pci \
|
||||||
-virtfs local,path=/nix/store,security_model=none,mount_tag=store \
|
-virtfs local,path=${storeDir},security_model=none,mount_tag=store \
|
||||||
-virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
|
-virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
|
||||||
-drive file=$diskImage,if=virtio,cache=unsafe,werror=report \
|
-drive file=$diskImage,if=virtio,cache=unsafe,werror=report \
|
||||||
-kernel ${kernel}/${img} \
|
-kernel ${kernel}/${img} \
|
||||||
@ -298,7 +299,7 @@ rec {
|
|||||||
|
|
||||||
/* Run a derivation in a Linux virtual machine (using Qemu/KVM). By
|
/* Run a derivation in a Linux virtual machine (using Qemu/KVM). By
|
||||||
default, there is no disk image; the root filesystem is a tmpfs,
|
default, there is no disk image; the root filesystem is a tmpfs,
|
||||||
and /nix/store is shared with the host (via the 9P protocol).
|
and the nix store is shared with the host (via the 9P protocol).
|
||||||
Thus, any pure Nix derivation should run unmodified, e.g. the
|
Thus, any pure Nix derivation should run unmodified, e.g. the
|
||||||
call
|
call
|
||||||
|
|
||||||
@ -434,8 +435,8 @@ rec {
|
|||||||
chroot=$(type -tP chroot)
|
chroot=$(type -tP chroot)
|
||||||
|
|
||||||
# Make the Nix store available in /mnt, because that's where the RPMs live.
|
# Make the Nix store available in /mnt, because that's where the RPMs live.
|
||||||
mkdir -p /mnt/nix/store
|
mkdir -p /mnt${storeDir}
|
||||||
${utillinux}/bin/mount -o bind /nix/store /mnt/nix/store
|
${utillinux}/bin/mount -o bind ${storeDir} /mnt${storeDir}
|
||||||
|
|
||||||
# Newer distributions like Fedora 18 require /lib etc. to be
|
# Newer distributions like Fedora 18 require /lib etc. to be
|
||||||
# symlinked to /usr.
|
# symlinked to /usr.
|
||||||
@ -474,7 +475,7 @@ rec {
|
|||||||
|
|
||||||
rm /mnt/.debug
|
rm /mnt/.debug
|
||||||
|
|
||||||
${utillinux}/bin/umount /mnt/nix/store /mnt/tmp ${lib.optionalString unifiedSystemDir "/mnt/proc"}
|
${utillinux}/bin/umount /mnt${storeDir} /mnt/tmp ${lib.optionalString unifiedSystemDir "/mnt/proc"}
|
||||||
${utillinux}/bin/umount /mnt
|
${utillinux}/bin/umount /mnt
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@ -605,8 +606,8 @@ rec {
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Make the Nix store available in /mnt, because that's where the .debs live.
|
# Make the Nix store available in /mnt, because that's where the .debs live.
|
||||||
mkdir -p /mnt/inst/nix/store
|
mkdir -p /mnt/inst${storeDir}
|
||||||
${utillinux}/bin/mount -o bind /nix/store /mnt/inst/nix/store
|
${utillinux}/bin/mount -o bind ${storeDir} /mnt/inst${storeDir}
|
||||||
${utillinux}/bin/mount -o bind /proc /mnt/proc
|
${utillinux}/bin/mount -o bind /proc /mnt/proc
|
||||||
${utillinux}/bin/mount -o bind /dev /mnt/dev
|
${utillinux}/bin/mount -o bind /dev /mnt/dev
|
||||||
|
|
||||||
@ -654,7 +655,7 @@ rec {
|
|||||||
|
|
||||||
rm /mnt/.debug
|
rm /mnt/.debug
|
||||||
|
|
||||||
${utillinux}/bin/umount /mnt/inst/nix/store
|
${utillinux}/bin/umount /mnt/inst${storeDir}
|
||||||
${utillinux}/bin/umount /mnt/proc
|
${utillinux}/bin/umount /mnt/proc
|
||||||
${utillinux}/bin/umount /mnt/dev
|
${utillinux}/bin/umount /mnt/dev
|
||||||
${utillinux}/bin/umount /mnt
|
${utillinux}/bin/umount /mnt
|
||||||
|
Loading…
Reference in New Issue
Block a user