Skip to content

Commit

Permalink
nix: create efficient oci images with reusable layers
Browse files Browse the repository at this point in the history
This set of functions generates layers that can be combined into OCI image layout directories.
Those can directly be pushed to a registry.
Strong focus was placed on making the layers reusable in arbitrary ways.
This allows for efficient distribution of binary artifacts via container image layers.
  • Loading branch information
malt3 committed Apr 2, 2024
1 parent 2ea21b1 commit 3c393fa
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 0 deletions.
28 changes: 28 additions & 0 deletions packages/by-name/ociImageConfig/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# application/vnd.oci.image.config.v1+json
{ runCommand, writers, lib, pkgs }:
{ layers ? [ ]
, extraConfig ? { }
}:
let
diffIDs = lib.lists.map (layer: builtins.readFile (layer + "/DiffID")) layers;
config = {
architecture = "amd64";
os = "linux";
} // extraConfig // {
rootfs = { type = "layers"; diff_ids = diffIDs; };
};
configJSON = writers.writeJSON "image-config.json" config;
in
runCommand "oci-image-config"
{
buildInputs = [ pkgs.nix ];
platformJSON = builtins.toJSON { inherit (config) architecture; inherit (config) os; };
inherit configJSON;
} ''
mkdir -p $out/blobs/sha256
sha256=$(nix-hash --type sha256 --flat $configJSON)
cp $configJSON "$out/blobs/sha256/$sha256"
ln -s "$out/blobs/sha256/$sha256" "$out/image-config.json"
echo "$platformJSON" > "$out/platform.json"
echo -n "{\"mediaType\": \"application/vnd.oci.image.config.v1+json\", \"size\": $(stat -c %s $configJSON), \"digest\": \"sha256:$sha256\"}" > $out/media-descriptor.json
''
36 changes: 36 additions & 0 deletions packages/by-name/ociImageLayout/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# OCI image layout. Can be pushed to a registry or used as a local image.
{ runCommand
, writers
, lib
, pkgs
}:
{ manifests ? [ ]
, extraIndex ? { }
}:
let
manifestDescriptors = lib.lists.map (manifest: builtins.fromJSON (builtins.readFile (manifest + "/media-descriptor.json"))) manifests;
index = writers.writeJSON "index.json" (
{
schemaVersion = 2;
mediaType = "application/vnd.oci.image.index.v1+json";
} // extraIndex // {
manifests = manifestDescriptors;
}
);
in
runCommand "oci-image-layout"
{
buildInputs = [ pkgs.nix ];
blobDirs = lib.lists.map (manifest: manifest + "/blobs/sha256") manifests;
inherit index;
} ''
srcs=($blobDirs)
mkdir -p $out/blobs/sha256
cp $index $out/index.json
echo '{"imageLayoutVersion": "1.0.0"}' > $out/image-layout
for src in $srcs; do
for blob in $(ls $src); do
ln -s "$(realpath $src/$blob)" "$out/blobs/sha256/$blob"
done
done
''
44 changes: 44 additions & 0 deletions packages/by-name/ociImageManifest/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# application/vnd.oci.image.manifest.v1+json
{ ociImageConfig
, runCommand
, writers
, lib
, pkgs
}:
{ layers ? [ ]
, extraConfig ? { }
, extraManifest ? { }
}:
let
config = ociImageConfig { inherit layers extraConfig; };
configDescriptor = builtins.fromJSON (builtins.readFile (config + "/media-descriptor.json"));
configPlatform = builtins.fromJSON (builtins.readFile (config + "/platform.json"));
layerDescriptors = lib.lists.map (layer: builtins.fromJSON (builtins.readFile (layer + "/media-descriptor.json"))) layers;
manifest = writers.writeJSON "image-manifest.json" (
{
schemaVersion = 2;
mediaType = "application/vnd.oci.image.manifest.v1+json";
} // extraManifest // {
config = configDescriptor;
layers = layerDescriptors;
}
);
in
runCommand "oci-image-manifest"
{
blobDirs = lib.lists.map (layer: layer + "/blobs/sha256") (layers ++ [ config ]);
platformJSON = builtins.toJSON configPlatform;
buildInputs = [ pkgs.nix ];
inherit manifest;
} ''
mkdir -p $out/blobs/sha256
sha256=$(nix-hash --type sha256 --flat $manifest)
cp $manifest "$out/blobs/sha256/$sha256"
ln -s "$out/blobs/sha256/$sha256" "$out/image-manifest.json"
echo -n "{\"mediaType\": \"application/vnd.oci.image.manifest.v1+json\", \"size\": $(stat -c %s $manifest), \"digest\": \"sha256:$sha256\", \"platform\": $platformJSON}" > $out/media-descriptor.json
for src in $blobDirs; do
for blob in $(ls $src); do
ln -s "$src/$blob" "$out/blobs/sha256/$blob"
done
done
''
42 changes: 42 additions & 0 deletions packages/by-name/ociLayerTar/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# application/vnd.oci.image.layer.v1.tar
# application/vnd.oci.image.layer.v1.tar+gzip
# application/vnd.oci.image.layer.v1.tar+zstd
{ runCommand, lib, pkgs }:
{ files ? [ ]
, compression ? "gzip"
}:
runCommand "ociLayer"
{
fileSources = lib.lists.map (file: file.source) files;
fileDestinations = lib.lists.map (file: file.destination or file.source) files;
outPath = "layer" + (if compression == "gzip" then ".tar.gz" else if compression == "zstd" then ".tar.zst" else ".tar");
mediaType = "application/vnd.oci.image.layer.v1.tar" + (if compression == "" then "" else "+" + compression);
buildInputs = [ pkgs.nix ] ++ lib.optional (compression == "gzip") pkgs.gzip ++ lib.optional (compression == "zstd") pkgs.zstd;
inherit compression;
} ''
set -o pipefail
srcs=($fileSources)
dests=($fileDestinations)
mkdir -p $out/root
for i in ''${!srcs[@]}; do
mkdir -p "$out/root/$(dirname ''${dests[$i]})"
cp -rT "''${srcs[i]}" "$out/root/''${dests[$i]}"
done
tar --sort=name --owner=root:0 --group=root:0 --mode=544 --mtime='UTC 1970-01-01' -cC $out/root -f $out/layer.tar .
diffID=$(nix-hash --type sha256 --flat $out/layer.tar)
if [ "$compression" = "gzip" ]; then
gzip -c $out/layer.tar > $out/$outPath
elif [ "$compression" = "zstd" ]; then
zstd -T0 -q -c $out/layer.tar > $out/$outPath
else
mv $out/layer.tar $out/$outPath
fi
rm -f $out/layer.tar
sha256=$(nix-hash --type sha256 --flat $out/$outPath)
echo -n "{\"mediaType\": \"$mediaType\", \"size\": $(stat -c %s $out/$outPath), \"digest\": \"sha256:$sha256\"}" > $out/media-descriptor.json
echo -n "sha256:$diffID" > $out/DiffID
mkdir -p $out/blobs/sha256
mv $out/$outPath $out/blobs/sha256/$sha256
ln -s $out/blobs/sha256/$sha256 $out/$outPath
rm -rf $out/root
''

0 comments on commit 3c393fa

Please sign in to comment.