···11# this test creates a simple GNU image with docker tools and sees if it executes
2233-import ./make-test-python.nix ({ pkgs, ... }: {
33+import ./make-test-python.nix ({ pkgs, ... }:
44+let
55+ # nixpkgs#214434: dockerTools.buildImage fails to unpack base images
66+ # containing duplicate layers when those duplicate tarballs
77+ # appear under the manifest's 'Layers'. Docker can generate images
88+ # like this even though dockerTools does not.
99+ repeatedLayerTestImage =
1010+ let
1111+ # Rootfs diffs for layers 1 and 2 are identical (and empty)
1212+ layer1 = pkgs.dockerTools.buildImage { name = "empty"; };
1313+ layer2 = layer1.overrideAttrs (_: { fromImage = layer1; });
1414+ repeatedRootfsDiffs = pkgs.runCommandNoCC "image-with-links.tar" {
1515+ nativeBuildInputs = [pkgs.jq];
1616+ } ''
1717+ mkdir contents
1818+ tar -xf "${layer2}" -C contents
1919+ cd contents
2020+ first_rootfs=$(jq -r '.[0].Layers[0]' manifest.json)
2121+ second_rootfs=$(jq -r '.[0].Layers[1]' manifest.json)
2222+ target_rootfs=$(sha256sum "$first_rootfs" | cut -d' ' -f 1).tar
2323+2424+ # Replace duplicated rootfs diffs with symlinks to one tarball
2525+ chmod -R ug+w .
2626+ mv "$first_rootfs" "$target_rootfs"
2727+ rm "$second_rootfs"
2828+ ln -s "../$target_rootfs" "$first_rootfs"
2929+ ln -s "../$target_rootfs" "$second_rootfs"
3030+3131+ # Update manifest's layers to use the symlinks' target
3232+ cat manifest.json | \
3333+ jq ".[0].Layers[0] = \"$target_rootfs\"" |
3434+ jq ".[0].Layers[1] = \"$target_rootfs\"" > manifest.json.new
3535+ mv manifest.json.new manifest.json
3636+3737+ tar --sort=name --hard-dereference -cf $out .
3838+ '';
3939+ in pkgs.dockerTools.buildImage {
4040+ fromImage = repeatedRootfsDiffs;
4141+ name = "repeated-layer-test";
4242+ tag = "latest";
4343+ copyToRoot = pkgs.bash;
4444+ # A runAsRoot script is required to force previous layers to be unpacked
4545+ runAsRoot = ''
4646+ echo 'runAsRoot has run.'
4747+ '';
4848+ };
4949+in {
450 name = "docker-tools";
551 meta = with pkgs.lib.maintainers; {
652 maintainers = [ lnl7 roberth ];
···219265 assert "abc" in docker.succeed(
220266 "docker load --input='${examples.layersUnpackOrder}'",
221267 "docker run --rm ${examples.layersUnpackOrder.imageName} cat /layer-order"
268268+ )
269269+270270+ with subtest("Ensure repeated base layers handled by buildImage"):
271271+ docker.succeed(
272272+ "docker load --input='${repeatedLayerTestImage}'",
273273+ "docker run --rm ${repeatedLayerTestImage.imageName} /bin/bash -c 'exit 0'"
222274 )
223275224276 with subtest("Ensure environment variables are correctly inherited"):
+11-1
pkgs/build-support/docker/default.nix
···229229 mount /dev/${vmTools.hd} disk
230230 cd disk
231231232232+ function dedup() {
233233+ declare -A seen
234234+ while read ln; do
235235+ if [[ -z "''${seen["$ln"]:-}" ]]; then
236236+ echo "$ln"; seen["$ln"]=1
237237+ fi
238238+ done
239239+ }
240240+232241 if [[ -n "$fromImage" ]]; then
233242 echo "Unpacking base image..."
234243 mkdir image
···245254 parentID="$(cat "image/manifest.json" | jq -r '.[0].Config | rtrimstr(".json")')"
246255 fi
247256248248- cat ./image/manifest.json | jq -r '.[0].Layers | .[]' > layer-list
257257+ # In case of repeated layers, unpack only the last occurrence of each
258258+ cat ./image/manifest.json | jq -r '.[0].Layers | .[]' | tac | dedup | tac > layer-list
249259 else
250260 touch layer-list
251261 fi